Loading libraries

library(GEOquery)
library(oligo)
Loading required package: oligoClasses
Welcome to oligoClasses version 1.56.0
Loading required package: Biostrings
Loading required package: XVector

Attaching package: ‘XVector’

The following object is masked from ‘package:purrr’:

    compact

Loading required package: GenomeInfoDb

Attaching package: ‘Biostrings’

The following object is masked from ‘package:base’:

    strsplit

================================================================
Welcome to oligo version 1.58.0
================================================================

Attaching package: ‘oligo’

The following object is masked from ‘package:dplyr’:

    summarize
library(sva)
Loading required package: mgcv
Loading required package: nlme

Attaching package: ‘nlme’

The following object is masked from ‘package:Biostrings’:

    collapse

The following object is masked from ‘package:dplyr’:

    collapse

The following object is masked from ‘package:IRanges’:

    collapse

This is mgcv 1.8-38. For overview type 'help("mgcv-package")'.
Loading required package: genefilter

Attaching package: ‘genefilter’

The following object is masked from ‘package:readr’:

    spec

Loading required package: BiocParallel
library(tidyverse)
library(ggsci)
library(factoextra)
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(pheatmap)
library(dendextend)

---------------------
Welcome to dendextend version 1.15.2
Type citation('dendextend') for how to cite the package.

Type browseVignettes(package = 'dendextend') for the package vignette.
The github page is: https://github.com/talgalili/dendextend/

Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
You may ask questions at stackoverflow, use the r and dendextend tags: 
     https://stackoverflow.com/questions/tagged/dendextend

    To suppress this message use:  suppressPackageStartupMessages(library(dendextend))
---------------------


Attaching package: ‘dendextend’

The following object is masked from ‘package:Biostrings’:

    nnodes

The following object is masked from ‘package:stats’:

    cutree
library(caret)
Loading required package: lattice

Attaching package: ‘caret’

The following object is masked from ‘package:purrr’:

    lift
library(RColorBrewer)
library(viridis)
Loading required package: viridisLite
library(UpSetR)

Attaching package: ‘UpSetR’

The following object is masked from ‘package:lattice’:

    histogram
library(ComplexUpset)

Attaching package: ‘ComplexUpset’

The following object is masked from ‘package:UpSetR’:

    upset

Custom functions

# given a matrix, perform min-max scaling on its columns
min_max_mat <- function(mat){
  mat_rescaled <- apply(mat, 2, function(v){
    v_range <- range(v)
    names(v_range) <- c("minimum", "maximum")
    range_difference <- v_range["maximum"] - v_range["minimum"]
    rescaled <- (v - v_range["minimum"])/range_difference
    return(rescaled)
  })
  return(mat_rescaled)
}

Getting data from GEOquery

# geodata <- GEOquery::getGEO(GEO = "GSE76275", destdir = "./tempfiles")
# geodata <- GEOquery::getGEO(filename = "./tempfiles/GSE76275_series_matrix.txt.gz")
# mdata <- geodata %>% 
#   pluck(1) %>% 
#   phenoData() %>%
#   pData() %>% as_tibble()
# feature_data <- geodata %>% 
#   pluck(1) %>% 
#   featureData()
  
# write_csv(mdata, "raw_mdata.csv")
mdata <- read_csv("raw_mdata.csv")
Rows: 265 Columns: 69
── Column specification ────────────────────────────────────────
Delimiter: ","
chr (62): title, geo_accession, status, submission_date, las...
dbl  (6): channel_count, taxid_ch1, contact_zip/postal_code,...
lgl  (1): growth_protocol_ch1

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Inspecting and cleaning the metadata

mdata %>% 
  glimpse()
Rows: 265
Columns: 23
$ title                                <chr> "S1-H10", "S1-H14…
$ submission_date                      <chr> "Dec 17 2015", "D…
$ last_update_date                     <chr> "Dec 18 2015", "D…
$ geo_accession                        <chr> "GSM1974566", "GS…
$ `age (years):ch1`                    <dbl> NA, 41, 55, 55, 6…
$ `ajcc stage (7th edition, 2010):ch1` <chr> "T2N1M0", "T1N0M0…
$ `body mass index:ch1`                <dbl> 32, 29, NA, 31, 3…
$ `er:ch1`                             <chr> "Negative", "Nega…
$ `gender:ch1`                         <chr> "Female", "Female…
$ `her2:ch1`                           <chr> "Negative", "Nega…
$ `histology group:ch1`                <chr> "Infiltrating Duc…
$ `histology:ch1`                      <chr> "Infiltrating Duc…
$ `menopausal status:ch1`              <chr> "Post-Menopausal"…
$ `metastases:ch1`                     <chr> "No mets", "No me…
$ `positive nodes:ch1`                 <chr> "1 - 3", "0", "0"…
$ `pr:ch1`                             <chr> "Negative", "Nega…
$ `race:ch1`                           <chr> "Caucasian", "Cau…
$ `set:ch1`                            <chr> "Validation TN", …
$ `tissue:ch1`                         <chr> "Breast cancer", …
$ `tnbc subtype:ch1`                   <chr> "Mesenchymal (MES…
$ `triple-negative status:ch1`         <chr> "TN", "TN", "TN",…
$ `tumor grade:ch1`                    <chr> NA, "Poorly Diffe…
$ `tumor size:ch1`                     <chr> "2 - 5 cm", "<=2c…
mdata <- mdata %>% 
  select(title, contains("date"), geo_accession, contains(":ch1"))

colnames(mdata)
 [1] "title"                             
 [2] "submission_date"                   
 [3] "last_update_date"                  
 [4] "geo_accession"                     
 [5] "age (years):ch1"                   
 [6] "ajcc stage (7th edition, 2010):ch1"
 [7] "body mass index:ch1"               
 [8] "er:ch1"                            
 [9] "gender:ch1"                        
[10] "her2:ch1"                          
[11] "histology group:ch1"               
[12] "histology:ch1"                     
[13] "menopausal status:ch1"             
[14] "metastases:ch1"                    
[15] "positive nodes:ch1"                
[16] "pr:ch1"                            
[17] "race:ch1"                          
[18] "set:ch1"                           
[19] "tissue:ch1"                        
[20] "tnbc subtype:ch1"                  
[21] "triple-negative status:ch1"        
[22] "tumor grade:ch1"                   
[23] "tumor size:ch1"                    
  
cnames <- colnames(mdata)
cnames_processed <- str_split(cnames, pattern = ":") %>% 
  map_chr(~{.x[[1]]}) %>% 
  str_replace_all(" ", "_") %>% 
  str_replace_all("-", "_") %>% 
  str_remove_all("\\(|\\)|,")

cnames_processed
 [1] "title"                       "submission_date"            
 [3] "last_update_date"            "geo_accession"              
 [5] "age_years"                   "ajcc_stage_7th_edition_2010"
 [7] "body_mass_index"             "er"                         
 [9] "gender"                      "her2"                       
[11] "histology_group"             "histology"                  
[13] "menopausal_status"           "metastases"                 
[15] "positive_nodes"              "pr"                         
[17] "race"                        "set"                        
[19] "tissue"                      "tnbc_subtype"               
[21] "triple_negative_status"      "tumor_grade"                
[23] "tumor_size"                 
colnames(mdata) <- cnames_processed
rm(cnames, cnames_processed)
glimpse(mdata)
Rows: 265
Columns: 23
$ title                       <chr> "S1-H10", "S1-H14", "S1-H1…
$ submission_date             <chr> "Dec 17 2015", "Dec 17 201…
$ last_update_date            <chr> "Dec 18 2015", "Dec 18 201…
$ geo_accession               <chr> "GSM1974566", "GSM1974567"…
$ age_years                   <dbl> NA, 41, 55, 55, 65, 40, 66…
$ ajcc_stage_7th_edition_2010 <chr> "T2N1M0", "T1N0M0", "T2N0M…
$ body_mass_index             <dbl> 32, 29, NA, 31, 38, 22, 22…
$ er                          <chr> "Negative", "Negative", "N…
$ gender                      <chr> "Female", "Female", "Femal…
$ her2                        <chr> "Negative", "Negative", "N…
$ histology_group             <chr> "Infiltrating Ductal Carci…
$ histology                   <chr> "Infiltrating Ductal Carci…
$ menopausal_status           <chr> "Post-Menopausal", "Post-M…
$ metastases                  <chr> "No mets", "No mets", "No …
$ positive_nodes              <chr> "1 - 3", "0", "0", "0", "4…
$ pr                          <chr> "Negative", "Negative", "N…
$ race                        <chr> "Caucasian", "Caucasian", …
$ set                         <chr> "Validation TN", "Validati…
$ tissue                      <chr> "Breast cancer", "Breast c…
$ tnbc_subtype                <chr> "Mesenchymal (MES)", "Basa…
$ triple_negative_status      <chr> "TN", "TN", "TN", "TN", "T…
$ tumor_grade                 <chr> NA, "Poorly Differentiated…
$ tumor_size                  <chr> "2 - 5 cm", "<=2cm", "2 - …
mdata <- mdata %>% 
  mutate(her2 = if_else(!is.na(her2), her2, "Not Available")) %>% 
  mutate(er = factor(er, levels = c("Negative", "Positive")), 
         pr = factor(pr, levels = c("Negative", "Positive")), 
         her2 = factor(her2, levels = c("Negative", "Positive", "Not Available"))) %>% 
  select(geo_accession, everything())
head(mdata) 

Reordering the columns in the metadata.

mdata <- select(mdata, geo_accession, everything())
head(mdata)

Reading in raw probe intensity data

Celfiles downloaded from GEO and kept the folder “celfiles/”.

celFiles <- list.celfiles('celfiles/', full.names = TRUE, listGzipped = TRUE)
celFiles %>% head()
[1] "celfiles//GSM1974566_S1_H10.CEL.gz" 
[2] "celfiles//GSM1974567_S1_H14.CEL.gz" 
[3] "celfiles//GSM1974568_S1_H19.CEL.gz" 
[4] "celfiles//GSM1974569_S1_H20B.CEL.gz"
[5] "celfiles//GSM1974570_S1_H22.CEL.gz" 
[6] "celfiles//GSM1974571_S1_H27.CEL.gz" 
names(celFiles) <- celFiles %>% 
  basename() %>% 
  str_split("\\.") %>% 
  map_chr(~{.x[1]}) %>% 
  str_split("_") %>% 
  map_chr(~{.x[1]}) 

head(celFiles)
                           GSM1974566 
 "celfiles//GSM1974566_S1_H10.CEL.gz" 
                           GSM1974567 
 "celfiles//GSM1974567_S1_H14.CEL.gz" 
                           GSM1974568 
 "celfiles//GSM1974568_S1_H19.CEL.gz" 
                           GSM1974569 
"celfiles//GSM1974569_S1_H20B.CEL.gz" 
                           GSM1974570 
 "celfiles//GSM1974570_S1_H22.CEL.gz" 
                           GSM1974571 
 "celfiles//GSM1974571_S1_H27.CEL.gz" 

Rearranging rows of metadata to match order of samples in celFiles.

mdata <- mdata[match(mdata$geo_accession, names(celFiles)), ]

Getting only the relevant variables from the metadata.

mdata_subset <- mdata %>%
  select(geo_accession, 
         title, 
         triple_negative_status, 
         tnbc_subtype,
         submission_date,
         er,
         her2,
         pr,
         race,
         set,
         gender, 
         age_years) %>% 
  mutate(across(where(is.character), .fns = factor)) %>% 
  mutate(tnbc_subtype = if_else(is.na(as.character(tnbc_subtype)), "Not Applicable", as.character(tnbc_subtype))) %>% 
  mutate(tnbc_subtype = factor(tnbc_subtype)) %>% 
  as.data.frame()


rownames(mdata_subset) <- as.character(mdata_subset$geo_accession)

head(mdata_subset)
NA
rawData <- read.celfiles(celFiles, phenoData = AnnotatedDataFrame(mdata_subset))
Platform design info loaded.
Reading in : celfiles//GSM1974566_S1_H10.CEL.gz
Reading in : celfiles//GSM1974567_S1_H14.CEL.gz
Reading in : celfiles//GSM1974568_S1_H19.CEL.gz
Reading in : celfiles//GSM1974569_S1_H20B.CEL.gz
Reading in : celfiles//GSM1974570_S1_H22.CEL.gz
Reading in : celfiles//GSM1974571_S1_H27.CEL.gz
Reading in : celfiles//GSM1974572_S1_H28.CEL.gz
Reading in : celfiles//GSM1974573_S1_H29.CEL.gz
Reading in : celfiles//GSM1974574_S1_H2B.CEL.gz
Reading in : celfiles//GSM1974575_S1_H31.CEL.gz
Reading in : celfiles//GSM1974576_S1_H35B.CEL.gz
Reading in : celfiles//GSM1974577_S1_H36.CEL.gz
Reading in : celfiles//GSM1974578_S1_H38.CEL.gz
Reading in : celfiles//GSM1974579_S1_H3B.CEL.gz
Reading in : celfiles//GSM1974580_S1_H40.CEL.gz
Reading in : celfiles//GSM1974581_S1_H41.CEL.gz
Reading in : celfiles//GSM1974582_S2_H43.CEL.gz
Reading in : celfiles//GSM1974583_S2_H44.CEL.gz
Reading in : celfiles//GSM1974584_S2_H45.CEL.gz
Reading in : celfiles//GSM1974585_S2_H46.CEL.gz
Reading in : celfiles//GSM1974586_S2_H47.CEL.gz
Reading in : celfiles//GSM1974587_S2_H48.CEL.gz
Reading in : celfiles//GSM1974588_S2_H49.CEL.gz
Reading in : celfiles//GSM1974589_S1_H4B.CEL.gz
Reading in : celfiles//GSM1974590_S2_H50.CEL.gz
Reading in : celfiles//GSM1974591_S2_H51.CEL.gz
Reading in : celfiles//GSM1974592_S2_H52.CEL.gz
Reading in : celfiles//GSM1974593_S2_H53.CEL.gz
Reading in : celfiles//GSM1974594_S2_H58B.CEL.gz
Reading in : celfiles//GSM1974595_S2_H59.CEL.gz
Reading in : celfiles//GSM1974596_S1_H6.CEL.gz
Reading in : celfiles//GSM1974597_S2_H60.CEL.gz
Reading in : celfiles//GSM1974598_S2_H61.CEL.gz
Reading in : celfiles//GSM1974599_S2_H62.CEL.gz
Reading in : celfiles//GSM1974600_S2_H63.CEL.gz
Reading in : celfiles//GSM1974601_S2_H64.CEL.gz
Reading in : celfiles//GSM1974602_S2_H65.CEL.gz
Reading in : celfiles//GSM1974603_S2_H66.CEL.gz
Reading in : celfiles//GSM1974604_S2_H67.CEL.gz
Reading in : celfiles//GSM1974605_S2_H68.CEL.gz
Reading in : celfiles//GSM1974606_S2_H69.CEL.gz
Reading in : celfiles//GSM1974607_S1_H7.CEL.gz
Reading in : celfiles//GSM1974608_S2_H70.CEL.gz
Reading in : celfiles//GSM1974609_S2_H71.CEL.gz
Reading in : celfiles//GSM1974610_S2_H72B.CEL.gz
Reading in : celfiles//GSM1974611_S2_H73.CEL.gz
Reading in : celfiles//GSM1974612_S1_H8.CEL.gz
Reading in : celfiles//GSM1974613_S1_H9.CEL.gz
Reading in : celfiles//GSM1974614_S2_H54B.CEL.gz
Reading in : celfiles//GSM1974615_S2_H55B.CEL.gz
Reading in : celfiles//GSM1974616_S2_H56B.CEL.gz
Reading in : celfiles//GSM1974617_S2_H57C.CEL.gz
Reading in : celfiles//GSM1974618_S2_H76.CEL.gz
Reading in : celfiles//GSM1974619_S2_H77.CEL.gz
Reading in : celfiles//GSM1974620_S2_H78.CEL.gz
Reading in : celfiles//GSM1974621_S2_H79.CEL.gz
Reading in : celfiles//GSM1974622_S2_H80.CEL.gz
Reading in : celfiles//GSM1974623_S2_H81.CEL.gz
Reading in : celfiles//GSM1974624_S2_H82.CEL.gz
Reading in : celfiles//GSM1974625_S2_H83.CEL.gz
Reading in : celfiles//GSM1974626_S2_H84.CEL.gz
Reading in : celfiles//GSM1974627_S2_H85B.CEL.gz
Reading in : celfiles//GSM1974628_S2_H88.CEL.gz
Reading in : celfiles//GSM1974629_S2_H89.CEL.gz
Reading in : celfiles//GSM1974630_S2_H90.CEL.gz
Reading in : celfiles//GSM1974631_S2_H91B.CEL.gz
Reading in : celfiles//GSM1974632_S3_H100C.CEL.gz
Reading in : celfiles//GSM1974633_S3_H102C.CEL.gz
Reading in : celfiles//GSM1974634_S3_H103C.CEL.gz
Reading in : celfiles//GSM1974635_S3_H104C.CEL.gz
Reading in : celfiles//GSM1974636_S3_H105C.CEL.gz
Reading in : celfiles//GSM1974637_S3_H106C.CEL.gz
Reading in : celfiles//GSM1974638_S3_H107C.CEL.gz
Reading in : celfiles//GSM1974639_S3_H108C.CEL.gz
Reading in : celfiles//GSM1974640_S3_H109C.CEL.gz
Reading in : celfiles//GSM1974641_S3_H110C.CEL.gz
Reading in : celfiles//GSM1974642_S3_H111C.CEL.gz
Reading in : celfiles//GSM1974643_S3_H113C.CEL.gz
Reading in : celfiles//GSM1974644_S3_H114C.CEL.gz
Reading in : celfiles//GSM1974645_S3_H115.CEL.gz
Reading in : celfiles//GSM1974646_S3_H116C.CEL.gz
Reading in : celfiles//GSM1974647_S3_H117C.CEL.gz
Reading in : celfiles//GSM1974648_S3_H118C.CEL.gz
Reading in : celfiles//GSM1974649_S3_H119C.CEL.gz
Reading in : celfiles//GSM1974650_S3_H120C.CEL.gz
Reading in : celfiles//GSM1974651_S3_H121C.CEL.gz
Reading in : celfiles//GSM1974652_S3_H122C.CEL.gz
Reading in : celfiles//GSM1974653_S3_H123C.CEL.gz
Reading in : celfiles//GSM1974654_S3_H124C.CEL.gz
Reading in : celfiles//GSM1974655_S3_H125C.CEL.gz
Reading in : celfiles//GSM1974656_S3_H126C.CEL.gz
Reading in : celfiles//GSM1974657_S3_H127C.CEL.gz
Reading in : celfiles//GSM1974658_S3_H128C.CEL.gz
Reading in : celfiles//GSM1974659_S3_H129C.CEL.gz
Reading in : celfiles//GSM1974660_S3_H130.CEL.gz
Reading in : celfiles//GSM1974661_S3_H131.CEL.gz
Reading in : celfiles//GSM1974662_S3_H132.CEL.gz
Reading in : celfiles//GSM1974663_S3_H133D.CEL.gz
Reading in : celfiles//GSM1974664_S3_H134.CEL.gz
Reading in : celfiles//GSM1974665_S3_H135B.CEL.gz
Reading in : celfiles//GSM1974666_S3_H136B.CEL.gz
Reading in : celfiles//GSM1974667_S3_H137B.CEL.gz
Reading in : celfiles//GSM1974668_S3_H138.CEL.gz
Reading in : celfiles//GSM1974669_S3_H139.CEL.gz
Reading in : celfiles//GSM1974670_S3_H140.CEL.gz
Reading in : celfiles//GSM1974671_S3_H141.CEL.gz
Reading in : celfiles//GSM1974672_S3_H142.CEL.gz
Reading in : celfiles//GSM1974673_S3_H143.CEL.gz
Reading in : celfiles//GSM1974674_S3_H144.CEL.gz
Reading in : celfiles//GSM1974675_S3_H145.CEL.gz
Reading in : celfiles//GSM1974676_S3_H146.CEL.gz
Reading in : celfiles//GSM1974677_S3_H147.CEL.gz
Reading in : celfiles//GSM1974678_S3_H148.CEL.gz
Reading in : celfiles//GSM1974679_S3_H149.CEL.gz
Reading in : celfiles//GSM1974680_S3_H150.CEL.gz
Reading in : celfiles//GSM1974681_S3_H151.CEL.gz
Reading in : celfiles//GSM1974682_S3_H152.CEL.gz
Reading in : celfiles//GSM1974683_S3_H153.CEL.gz
Reading in : celfiles//GSM1974684_S3_H154.CEL.gz
Reading in : celfiles//GSM1974685_S3_H155.CEL.gz
Reading in : celfiles//GSM1974686_S3_H156.CEL.gz
Reading in : celfiles//GSM1974687_S3_H157.CEL.gz
Reading in : celfiles//GSM1974688_S3_H158.CEL.gz
Reading in : celfiles//GSM1974689_S3_H159.CEL.gz
Reading in : celfiles//GSM1974690_S3_H160.CEL.gz
Reading in : celfiles//GSM1974691_S3_H161.CEL.gz
Reading in : celfiles//GSM1974692_S3_H162.CEL.gz
Reading in : celfiles//GSM1974693_S3_H163.CEL.gz
Reading in : celfiles//GSM1974694_S3_H164C.CEL.gz
Reading in : celfiles//GSM1974695_S3_H165.CEL.gz
Reading in : celfiles//GSM1974696_S3_H166.CEL.gz
Reading in : celfiles//GSM1974697_S3_H167.CEL.gz
Reading in : celfiles//GSM1974698_S3_H168.CEL.gz
Reading in : celfiles//GSM1974699_S3_H170.CEL.gz
Reading in : celfiles//GSM1974700_S3_H171.CEL.gz
Reading in : celfiles//GSM1974701_S3_H172B.CEL.gz
Reading in : celfiles//GSM1974702_S3_H173.CEL.gz
Reading in : celfiles//GSM1974703_S3_H174.CEL.gz
Reading in : celfiles//GSM1974704_S3_H175B.CEL.gz
Reading in : celfiles//GSM1974705_S3_H176.CEL.gz
Reading in : celfiles//GSM1974706_S3_H177.CEL.gz
Reading in : celfiles//GSM1974707_S3_H178.CEL.gz
Reading in : celfiles//GSM1974708_S3_H179.CEL.gz
Reading in : celfiles//GSM1974709_S3_H180.CEL.gz
Reading in : celfiles//GSM1974710_S3_H181.CEL.gz
Reading in : celfiles//GSM1974711_S3_H182.CEL.gz
Reading in : celfiles//GSM1974712_S3_H183.CEL.gz
Reading in : celfiles//GSM1974713_S3_H184.CEL.gz
Reading in : celfiles//GSM1974714_S3_H185.CEL.gz
Reading in : celfiles//GSM1974715_S3_H186.CEL.gz
Reading in : celfiles//GSM1974716_S3_H187B.CEL.gz
Reading in : celfiles//GSM1974717_S3_H188.CEL.gz
Reading in : celfiles//GSM1974718_S3_H189.CEL.gz
Reading in : celfiles//GSM1974719_S3_H190.CEL.gz
Reading in : celfiles//GSM1974720_S3_H191B.CEL.gz
Reading in : celfiles//GSM1974721_S3_H193.CEL.gz
Reading in : celfiles//GSM1974722_S3_H194.CEL.gz
Reading in : celfiles//GSM1974723_S3_H196.CEL.gz
Reading in : celfiles//GSM1974724_S3_H197.CEL.gz
Reading in : celfiles//GSM1974725_S3_H198.CEL.gz
Reading in : celfiles//GSM1974726_S3_H199.CEL.gz
Reading in : celfiles//GSM1974727_S3_H200.CEL.gz
Reading in : celfiles//GSM1974728_S3_H201.CEL.gz
Reading in : celfiles//GSM1974729_S3_H202.CEL.gz
Reading in : celfiles//GSM1974730_S3_H203.CEL.gz
Reading in : celfiles//GSM1974731_S3_H204.CEL.gz
Reading in : celfiles//GSM1974732_S3_H205B.CEL.gz
Reading in : celfiles//GSM1974733_S3_H206B.CEL.gz
Reading in : celfiles//GSM1974734_S3_H207B.CEL.gz
Reading in : celfiles//GSM1974735_S3_H208B.CEL.gz
Reading in : celfiles//GSM1974736_S3_H209B.CEL.gz
Reading in : celfiles//GSM1974737_S3_H210B.CEL.gz
Reading in : celfiles//GSM1974738_S3_H211B.CEL.gz
Reading in : celfiles//GSM1974739_S3_H212.CEL.gz
Reading in : celfiles//GSM1974740_S3_H213.CEL.gz
Reading in : celfiles//GSM1974741_S3_H214.CEL.gz
Reading in : celfiles//GSM1974742_S3_H215.CEL.gz
Reading in : celfiles//GSM1974743_S3_H216.CEL.gz
Reading in : celfiles//GSM1974744_S3_H217.CEL.gz
Reading in : celfiles//GSM1974745_S3_H218.CEL.gz
Reading in : celfiles//GSM1974746_S3_H219.CEL.gz
Reading in : celfiles//GSM1974747_S3_H220.CEL.gz
Reading in : celfiles//GSM1974748_S3_H221.CEL.gz
Reading in : celfiles//GSM1974749_S3_H222.CEL.gz
Reading in : celfiles//GSM1974750_S3_H224.CEL.gz
Reading in : celfiles//GSM1974751_S3_H225B.CEL.gz
Reading in : celfiles//GSM1974752_S3_H226B.CEL.gz
Reading in : celfiles//GSM1974753_S3_H227B.CEL.gz
Reading in : celfiles//GSM1974754_S3_H228.CEL.gz
Reading in : celfiles//GSM1974755_S3_H229.CEL.gz
Reading in : celfiles//GSM1974756_S3_H230.CEL.gz
Reading in : celfiles//GSM1974757_S3_H93C.CEL.gz
Reading in : celfiles//GSM1974758_S3_H94C.CEL.gz
Reading in : celfiles//GSM1974759_S3_H95C.CEL.gz
Reading in : celfiles//GSM1974760_S3_H96C.CEL.gz
Reading in : celfiles//GSM1974761_S3_H97CC.CEL.gz
Reading in : celfiles//GSM1974762_S3_H98C.CEL.gz
Reading in : celfiles//GSM1974763_S3_H99C.CEL.gz
Reading in : celfiles//GSM1978883_S1_H11.CEL.gz
Reading in : celfiles//GSM1978884_S1_H12.CEL.gz
Reading in : celfiles//GSM1978885_S1_H13.CEL.gz
Reading in : celfiles//GSM1978886_S1_H15.CEL.gz
Reading in : celfiles//GSM1978887_S1_H16.CEL.gz
Reading in : celfiles//GSM1978888_S1_H17.CEL.gz
Reading in : celfiles//GSM1978889_S1_H18.CEL.gz
Reading in : celfiles//GSM1978890_S1_H1B.CEL.gz
Reading in : celfiles//GSM1978891_S1_H21.CEL.gz
Reading in : celfiles//GSM1978892_S1_H23.CEL.gz
Reading in : celfiles//GSM1978893_S1_H25.CEL.gz
Reading in : celfiles//GSM1978894_S1_H30.CEL.gz
Reading in : celfiles//GSM1978895_S1_H32.CEL.gz
Reading in : celfiles//GSM1978896_S1_H37.CEL.gz
Reading in : celfiles//GSM1978897_S1_H39.CEL.gz
Reading in : celfiles//GSM1978898_S1_H42.CEL.gz
Reading in : celfiles//GSM1978899_S1_H5.CEL.gz
Reading in : celfiles//GSM1978900_S3_H195.CEL.gz
Reading in : celfiles//GSM1978901_S3_H223.CEL.gz
Reading in : celfiles//GSM1978902_S4_H231.CEL.gz
Reading in : celfiles//GSM1978903_S4_H232.CEL.gz
Reading in : celfiles//GSM1978904_S4_H233.CEL.gz
Reading in : celfiles//GSM1978905_S4_H234.CEL.gz
Reading in : celfiles//GSM1978906_S4_H235.CEL.gz
Reading in : celfiles//GSM1978907_S4_H236.CEL.gz
Reading in : celfiles//GSM1978908_S4_H237.CEL.gz
Reading in : celfiles//GSM1978909_S4_H238.CEL.gz
Reading in : celfiles//GSM1978910_S4_H239.CEL.gz
Reading in : celfiles//GSM1978911_S4_H240.CEL.gz
Reading in : celfiles//GSM1978912_S4_H241.CEL.gz
Reading in : celfiles//GSM1978913_S4_H242.CEL.gz
Reading in : celfiles//GSM1978914_S4_H243.CEL.gz
Reading in : celfiles//GSM1978915_S4_H244.CEL.gz
Reading in : celfiles//GSM1978916_S4_H245.CEL.gz
Reading in : celfiles//GSM1978917_S4_H246.CEL.gz
Reading in : celfiles//GSM1978918_S4_H247.CEL.gz
Reading in : celfiles//GSM1978919_S4_H248.CEL.gz
Reading in : celfiles//GSM1978920_S4_H249.CEL.gz
Reading in : celfiles//GSM1978921_S4_H250.CEL.gz
Reading in : celfiles//GSM1978922_S4_H251.CEL.gz
Reading in : celfiles//GSM1978923_S4_H252.CEL.gz
Reading in : celfiles//GSM1978924_S4_H253.CEL.gz
Reading in : celfiles//GSM1978925_S4_H254.CEL.gz
Reading in : celfiles//GSM1978926_S4_H255.CEL.gz
Reading in : celfiles//GSM1978927_S4_H256.CEL.gz
Reading in : celfiles//GSM1978928_S4_H257B.CEL.gz
Reading in : celfiles//GSM1978929_S4_H258B.CEL.gz
Reading in : celfiles//GSM1978930_S4_H259.CEL.gz
Reading in : celfiles//GSM1978931_S4_H261.CEL.gz
Reading in : celfiles//GSM1978932_S4_H262.CEL.gz
Reading in : celfiles//GSM1978933_S4_H263.CEL.gz
Reading in : celfiles//GSM1978934_S4_H264.CEL.gz
Reading in : celfiles//GSM1978935_S4_H265.CEL.gz
Reading in : celfiles//GSM1978936_S4_H266.CEL.gz
Reading in : celfiles//GSM1978937_S4_H267.CEL.gz
Reading in : celfiles//GSM1978938_S4_H268.CEL.gz
Reading in : celfiles//GSM1978939_S4_H269.CEL.gz
Reading in : celfiles//GSM1978940_S4_H270.CEL.gz
Reading in : celfiles//GSM1978941_S4_H271.CEL.gz
Reading in : celfiles//GSM1978942_S4_H272.CEL.gz
Reading in : celfiles//GSM1978943_S4_H273.CEL.gz
Reading in : celfiles//GSM1978944_S4_H274.CEL.gz
Reading in : celfiles//GSM1978945_S4_H275B.CEL.gz
Reading in : celfiles//GSM1978946_S4_H276B.CEL.gz
Reading in : celfiles//GSM1978947_S4_H278.CEL.gz
Reading in : celfiles//GSM1978948_S4_H279B.CEL.gz
Reading in : celfiles//GSM1978949_S4_H280B.CEL.gz
Warning in read.celfiles(celFiles, phenoData = AnnotatedDataFrame(mdata_subset)) :
  'channel' automatically added to varMetadata in phenoData.

Looking at the dimensions of the raw expression matrix.

exprs(rawData) %>% dim()
[1] 1354896     265

Plotting some metadata attributes

Looking at the number of samples for triple negative status and for set.

mdata_subset %>% 
  count(triple_negative_status, set)
mdata_subset %>% 
  count(triple_negative_status) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(y = "dummy_group",
                         x = proportion,
                         fill = triple_negative_status)) +
    theme(axis.text.x = element_text(angle = 90, size = 7),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank(),
          title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "horizontal", legend.position = "top") +
  geom_text(aes(y = 1, 
                x = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 5, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
  scale_fill_npg(name = "Triple Negative Status") +
  guides(fill = guide_legend(reverse = TRUE)) +
  labs(x = "Proportion",
       title = str_wrap("Proportions of TNBC status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_tnbc_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, set) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = triple_negative_status,
                         y = proportion,
                         fill = set)) +
    geom_text(aes(x = triple_negative_status, 
                y = proportion, 
                label = proportion, 
                group = set), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Sample Set") +
  labs(x = "TNBC Status",
       y = "Proportion",
       title = str_wrap("Proportions of sample set values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_set_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  filter(triple_negative_status == "not TN") %>% 
  count(pr) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(y = "dummy_group",
                         x = proportion,
                         fill = pr )) +
    theme(axis.text.x = element_text(angle = 90, size = 7),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank(),
          title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "horizontal", legend.position = "top") +
  geom_text(aes(y = 1, 
                x = proportion, 
                label = proportion, 
                group = pr ), 
            size = 5, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
  scale_fill_npg(name = "Progesterone Receptor Status") +
  guides(fill = guide_legend(reverse = TRUE)) +
  labs(x = "Proportion",
       title = str_wrap("Proportions of progesterone receptor status values for non-TNBC samples in GSE7627", 60))


ggsave(filename = "plots/exploration_plots/GSE76275_pr_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  filter(triple_negative_status == "not TN") %>% 
  count(er) %>%  
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(y = "dummy_group",
                         x = proportion,
                         fill = er)) +
    theme(axis.text.x = element_text(angle = 90, size = 7),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank(),
          title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "horizontal", legend.position = "top") +
  geom_text(aes(y = 1, 
                x = proportion, 
                label = proportion, 
                group = er), 
            size = 5, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
  scale_fill_npg(name = "Estrogen Receptor Status") +
  guides(fill = guide_legend(reverse = TRUE)) +
  labs(x = "Proportion",
       title = str_wrap("Proportions of estrogen receptor status values for non-TNBC samples in GSE7627", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_er_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  filter(triple_negative_status == "not TN") %>% 
  count(her2) %>%  
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(y = "dummy_group",
                         x = proportion,
                         fill = her2)) +
    theme(axis.text.x = element_text(angle = 90, size = 7),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank(),
          title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "horizontal", legend.position = "top") +
  geom_text(aes(y = 1, 
                x = proportion, 
                label = proportion, 
                group = her2), 
            size = 5, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
  scale_fill_npg(name = "HER2 Amplification Status") +
  guides(fill = guide_legend(reverse = TRUE)) +
  labs(x = "Proportion",
       title = str_wrap("Proportions of HER2 amplification status values for non-TNBC samples in GSE7627", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_her2_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  ggplot() +
  geom_boxplot(aes(x = triple_negative_status, y = age_years, fill = set)) +
  scale_fill_npg(name = "Sample Set") +
  theme_light() +
    labs(x = "Triple Negative Status",
         y = "Age in Years",
         title = str_wrap("Age distribution of sample sets for GSE76275", 60))
Warning: Removed 9 rows containing non-finite values (stat_boxplot).
  

ggsave("plots/exploration_plots/GSE76275_age_boxplot.png")
Saving 5.03 x 3.11 in image
Warning: Removed 9 rows containing non-finite values (stat_boxplot).

list("ER" = mdata_subset$geo_accession[mdata_subset$er == "Positive"],
     "PR" = mdata_subset$geo_accession[mdata_subset$pr == "Positive"],
     "HER2" = mdata_subset$geo_accession[mdata_subset$her2 == "Positive"]) %>%
  fromList(.) %>% 
  upset(., c("ER", "PR", "HER2"), width_ratio = 0.2) +
  ggtitle(str_wrap("Upset plot for different combinations of ER, PR, and HER2 status in the non-TNBC samples in GSE76275", 40)) +
  theme(title = element_text(size = 10))

ggsave("plots/exploration_plots/GSE76275_nonTNBC_upset.png")
Saving 5.03 x 3.11 in image

Performing RMA

Using regular RMA on data (without separating by class)

res_1 <- rma(rawData)
Loading required package: RSQLite
Loading required package: DBI
Background correcting
Normalizing
Calculating Expression

Looking at the dimensions of the expression matrix after RMA.

exprs(res_1) %>% 
  dim()
[1] 54675   265

Looking at the first 5 rows and columns.

exprs(res_1)[1:5, 1:5]
          GSM1974566 GSM1974567 GSM1974568 GSM1974569
1007_s_at  10.754163  11.361803   9.690693  10.157010
1053_at     8.625663   9.011796   7.854072   7.925281
117_at      7.333973   7.385199   7.466878   8.048563
121_at      9.077887   8.782080   8.885189   8.775218
1255_g_at   4.656331   4.635625   4.536114   4.626950
          GSM1974570
1007_s_at  10.597699
1053_at     8.456781
117_at      7.581895
121_at      8.752407
1255_g_at   5.454820

Performing class-specific RMA by reading in the expression sets separately

Getting lists of the TNBC samples and the non-TNBC samples.

tnbc_samples <- mdata_subset %>% 
  filter(triple_negative_status == "TN") %>% 
  select(geo_accession) %>% 
  unlist(use.names = F) %>% 
  as.character()

head(tnbc_samples)
[1] "GSM1974566" "GSM1974567" "GSM1974568" "GSM1974569"
[5] "GSM1974570" "GSM1974571"
nontnbc_samples <- mdata_subset %>% 
  filter(triple_negative_status == "not TN") %>% 
  select(geo_accession) %>% 
  unlist(use.names = F) %>% 
  as.character()

head(nontnbc_samples)
[1] "GSM1978883" "GSM1978884" "GSM1978885" "GSM1978886"
[5] "GSM1978887" "GSM1978888"

Creating different metadata tables for TNBC and nonTNBC.

mdata_subset_tnbc <- mdata_subset[tnbc_samples, ]
dim(mdata_subset_tnbc)
[1] 198  12
mdata_subset_nontnbc <- mdata_subset[nontnbc_samples, ]
dim(mdata_subset_nontnbc)
[1] 67 12

Reading in the TNBC files.

rawData_tnbc <- read.celfiles(filenames = celFiles[tnbc_samples],
                              phenoData = AnnotatedDataFrame(mdata_subset_tnbc))

rawData_tnbc

Reading in the nonTNBC files.

rawData_nontnbc <- read.celfiles(filenames = celFiles[nontnbc_samples],
                              phenoData = AnnotatedDataFrame(mdata_subset_nontnbc))

rawData_nontnbc
rawData_nontnbc
ExpressionFeatureSet (storageMode: lockedEnvironment)
assayData: 1354896 features, 67 samples 
  element names: exprs 
protocolData
  rowNames: GSM1978883 GSM1978884 ... GSM1978949 (67
    total)
  varLabels: exprs dates
  varMetadata: labelDescription channel
phenoData
  rowNames: GSM1978883 GSM1978884 ... GSM1978949 (67
    total)
  varLabels: geo_accession title ... age_years (11
    total)
  varMetadata: labelDescription channel
featureData: none
experimentData: use 'experimentData(object)'
Annotation: pd.hg.u133.plus.2 

Performing RMA on TNBC data.

res_tnbc <- rma(rawData_tnbc)

Performing RMA on nonTNBC data.

res_nontnbc <- rma(rawData_nontnbc)

Combining the expression matrices of TNBC and nonTNBC data after separate RMA.

res_joint <- cbind(exprs(res_tnbc), exprs(res_nontnbc))

Looking at the first 5 rows and columns of the combined expression matrices.

res_joint[1:5, 1:5]
          GSM1974566 GSM1974567 GSM1974568 GSM1974569
1007_s_at  10.703231  11.345325   9.732361  10.175704
1053_at     8.605269   9.019553   7.794234   7.945029
117_at      7.360884   7.373951   7.481344   8.047586
121_at      9.100729   8.776369   8.860402   8.755606
1255_g_at   4.664492   4.628692   4.569530   4.627230
          GSM1974570
1007_s_at  10.576463
1053_at     8.497373
117_at      7.530427
121_at      8.766560
1255_g_at   5.467292

Saving certain CSV files for everyone else to refer to

Saving the joint expression matrix from class-specific QN.

res_joint %>% 
  as_tibble(rownames = "probe_id") %>% 
  write_csv("dataframe_files/post_classQN_expression.csv")
Error: Cannot open file for writing:
* 'dataframe_files/post_classQN_expression.csv'

Saving a subset of the metadata that I think is relevant.

mdata_subset %>% 
  write_csv("dataframe_files/metadata_subset.csv")

Saving the TNBC and nonTNBC metadata separately, just in case.

mdata_subset_tnbc %>% 
  write_csv("dataframe_files/metadata_subset_tnbc.csv")
mdata_subset_nontnbc %>% 
  write_csv("dataframe_files/metadata_subset_nontnbc.csv")

Getting sample-specific boxplots

Sample specific boxplots for regular RMA QN

res_1_df_long <- res_1 %>%
  exprs() %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity") %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession"))
p2 <- res_1_df_long %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, color = set), outlier.size = 0.2)
p2 <- p2 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for global quantile normalization for GSE76275", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")
p2

NA
ggsave("plots/exploration_plots/GSE76275_post_regQN_boxplots.png", 
       p2, 
       units = "cm", 
       width = 30, 
       height = 10)
rm(res_1_df_long)

Sample specific boxplots for classQN

res_joint_df_long <- res_joint %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity")
Error in as_tibble(., rownames = "probeID") : 
  object 'res_joint' not found
saveRDS(object = res_joint_df_long, "res_joint_df_long.RDS")
Error in saveRDS(object = res_joint_df_long, "res_joint_df_long.RDS") : 
  object 'res_joint_df_long' not found
p1 <- res_joint_df_long %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession")) %>% 
  mutate(sample_id = factor(sample_id)) %>% 
  ggplot() +
   geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, color = set), outlier.size = 0.2)
p1 <- p1 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for class-specific quantile normalization for GSE76275", 60)) +
scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")

p1 

ggsave("plots/exploration_plots/GSE76275_post_classQN_boxplots.png", 
       p1, 
       units = "cm", width = 20, height = 10)

Performing PCA

Custom functions

Function to create an annotated data frame by combining PC scores as well as metadata: useful for ggplot visualization.

get_pca_annot_df <- function(pca.obj, sample_id_col, mdata_df){
  ind_scores <- pca.obj$x
  ind_scores_reordered <- ind_scores[match(rownames(ind_scores), mdata_df[[sample_id_col]]), ] %>% 
    as_tibble(rownames = sample_id_col) %>% 
    mutate(filename = factor(!!sym(sample_id_col)))
  ind_scores_annot <- left_join(ind_scores_reordered, y = mdata_df, by = sample_id_col) %>% 
  select(all_of(colnames(mdata_subset)), contains("PC"))
  return(ind_scores_annot)
}

Performing PCA on regular RMA data

pca.res_1 <- res_1 %>%
  exprs() %>%
  t() %>%
  prcomp(center = TRUE, scale = TRUE)

Getting the annotated data frame for the PCA.

pca.res_1.annot_df <- get_pca_annot_df(pca.obj = pca.res_1, sample_id_col = "geo_accession", mdata_df= mdata_subset)
head(pca.res_1.annot_df)

Visualizing PCA results

Looking at the variance explained by the first 10 PCs.

fviz_eig(pca.res_1) +
  labs(x = "Principal Component", 
       title = str_wrap("Scree plot for the first 10 principal components for global RMA-normalized data for GSE76275", 60)) +
    theme(title = element_text(size = 10), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_scree.png", bg = "white")
Saving 5.03 x 3.11 in image

Superimposing variables in data upon sample PCA scores.

The PCA does not seem to separate the TNBC and nonTNBC samples that well when regular RMA is performed.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by triple negative status for GSE76275 after global quantile normalization", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))


ggsave("plots/exploration_plots/PCA_wholeQN_TNBC_status.png", bg = "white")
Saving 5.03 x 3.11 in image

NA

The samples do not seem to separate well by set either.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = set)) +
  ggtitle(str_wrap("Samples in first two PCs, coloured by set (discovery or validation) for GSE76275 after global quantile normalization", 60)) +
  scale_color_aaas(name = "Sample Set") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_set.png", bg = "white")
Saving 5.03 x 3.11 in image

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = tnbc_subtype)) +
    ggtitle("Samples in first two PCs, \ncoloured by tnbc_subtype for whole QN") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5), legend.position = "right", legend.direction = "vertical", legend.key.width = unit(x = 0.5, units = "cm")) 

Performing PCA on separately-performed RMA data

pca.res_joint <- res_joint %>% 
  t() %>% 
  prcomp(center = TRUE, scale = TRUE)
# saveRDS(pca.res_joint, "pca_res_joint.RDS")
pca.res_joint <- readRDS("pca_res_joint.RDS")

Getting the annotated data frame for the PCA.

pca.res_joint.annot_df <- get_pca_annot_df(pca.obj = pca.res_joint, sample_id_col = "geo_accession", mdata_df= mdata_subset)
head(pca.res_joint.annot_df)

Visualizing PCA results

Looking at the variance explained by the first 10 PCs.

fviz_eig(pca.res_joint) +
  labs(x = "Principal Component", 
       title = str_wrap("Scree plot for the first 10 principal components for class-specific RMA-normalized data for GSE76275", 60)) +
    theme(title = element_text(size = 10), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_scree.png")
Saving 5.03 x 3.11 in image

Superimposing variables in data upon sample PCA scores. The PCA does separate the TNBC and nonTNBC samples well when class-specific RMA is performed.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by triple negative status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_status.png")
Saving 5.03 x 3.11 in image

The validation nonTNBC samples are separated from the discovery TNBC and validation TNBC samples.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = set)) +
  ggtitle(str_wrap("Samples in first two PCs, coloured by set (discovery or validation) for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_aaas(name = "Sample Set") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
        title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_set.png")
Saving 5.03 x 3.11 in image

Submission date is perfectly confounded with TNBC status. May or may not be batch effects.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = submission_date)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by submission date for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Submission Date") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_date.png")
Saving 5.03 x 3.11 in image

Performing hierarchical clustering

Getting distances

perform_min_max <- function(x){
  mm_transformation <- preProcess(x, method = "range")
  rescaled <- predict(mm_transformation, x)
  return(rescaled)
}

Getting distances after performing min max normalization.

res_joint_dists <- res_joint %>%
    t() %>%
  perform_min_max() %>%
  dist(method = "euclidean")
  

Using heatmaps

Function to process distance object into a distance matrix for heatmap visualization.

get_distmat <- function(x){
  distmat <- as.matrix(x)
  colnames(distmat) <- NULL
  diag(distmat) <- NA
  return(distmat)
}
row_annot <- mdata_subset %>% 
  mutate(er = factor(er), pr = factor(pr), her2 = factor(her2)) %>% 
  select(set, submission_date, pr, er, her2) %>% 
    rename(`Sample set` = set, 
           `Submission date` = submission_date,
           `HER2 status` = her2,
           `ER status` = er,
           `PR status` = pr)

head(row_annot)
set.seed(5)
row_colours <- list( "Sample set" = c("darkgoldenrod4", "darkmagenta", "salmon"), 
                     "Submission date" = pal_nejm()(2),
                     "PR status" = pal_lancet()(9)[1:2], 
                     "ER status" = pal_lancet()(9)[8:9],
                     "HER2 status" = pal_npg()(9)[c(3, 2, 8)])

names(row_colours[["Sample set"]]) <- as.character(unique(row_annot[["Sample set"]]))
names(row_colours[["PR status"]]) <- unique(row_annot[["PR status"]])
names(row_colours[["ER status"]]) <- unique(row_annot[["ER status"]])
names(row_colours[["HER2 status"]]) <- unique(row_annot[["HER2 status"]])
names(row_colours[["Submission date"]]) <- as.character(unique(row_annot[["Submission date"]]))
str(row_colours)
List of 5
 $ Sample set     : Named chr [1:3] "darkgoldenrod4" "darkmagenta" "salmon"
  ..- attr(*, "names")= chr [1:3] "Validation TN" "Discovery TN" "Validation not TN"
 $ Submission date: Named chr [1:2] "#BC3C29FF" "#0072B5FF"
  ..- attr(*, "names")= chr [1:2] "Dec 17 2015" "Dec 22 2015"
 $ PR status      : Named chr [1:2] "#00468BFF" "#ED0000FF"
  ..- attr(*, "names")= chr [1:2] "Negative" "Positive"
 $ ER status      : Named chr [1:2] "#ADB6B6FF" "#1B1919FF"
  ..- attr(*, "names")= chr [1:2] "Negative" "Positive"
 $ HER2 status    : Named chr [1:3] "#00A087FF" "#4DBBD5FF" "#DC0000FF"
  ..- attr(*, "names")= chr [1:3] "Negative" "Positive" "Not Available"
my_colours <-  viridis(265^2, begin = 1, end = 0)
res_joint_dists %>% 
  get_distmat() %>% 
pheatmap(.,
         color = my_colours,
         annotation_row = row_annot,
         annotation_colors = row_colours,
         show_colnames = F,
         show_rownames = F,
         cutree_rows = 2,
         cutree_cols = 2,
         main = str_wrap("Heatmap of sample distances for class-specific QN expression matrix for GSE76275", 60),
         legend_labels = c("small distance", "large distance"),
         legend_breaks = c(min(., na.rm = TRUE), 
                         max(., na.rm = TRUE)), 
          filename = "plots/exploration_plots/classQN_clustering_heatmap_GSE76275.png")

Performing SVA

In this attempt, I perform no quantile normalization while performing RMA. If QN has not been performed and a surrogate variable shows up that corresponds to batch, batch effects are probably present.

rawData.summary <- rma(rawData, background = TRUE, normalize = FALSE)
Loading required package: RSQLite
Loading required package: DBI
Background correcting
Calculating Expression
n.sv.wholeQN <- num.sv(exprs(res_1), full_mod, method="leek")
p3 <- rawData.summary_df_long %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, 
                             color = set))
p3 <- p3 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for non- quantile normalized expression values for GSE76275", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")

p3

ggsave("plots/exploration_plots/GSE76275_noQN_boxplots.png", 
       p3, 
       units = "cm", width = 30, height = 10)

Create full model matrix.

full_mod <- mdata_subset %>% 
  select(geo_accession, triple_negative_status) %>% 
  arrange(triple_negative_status) %>% 
  model.matrix(~triple_negative_status, data = .)

head(full_mod)
           (Intercept) triple_negative_statusTN
GSM1978883           1                        0
GSM1978884           1                        0
GSM1978885           1                        0
GSM1978886           1                        0
GSM1978887           1                        0
GSM1978888           1                        0

Create reduced model matrix.

red_mod <- model.matrix(~1, data = mdata_subset)

head(red_mod)
           (Intercept)
GSM1974566           1
GSM1974567           1
GSM1974568           1
GSM1974569           1
GSM1974570           1
GSM1974571           1

Getting the number of surrogate variables in the absence of quantile normalization.

n.sv.nonorm <- num.sv(exprs(rawData.summary), full_mod, method="leek")

There is one surrogate variable present in the absence of QN.

n.sv.nonorm
[1] 1
svobj.nonorm <- sva(exprs(rawData.summary), mod = full_mod, mod0 = red_mod, n.sv = n.sv.nonorm)
Number of significant surrogate variables is:  1 
Iteration (out of 5 ):1  2  3  4  5  
sv_df.nonorm <- tibble("geo_accession" = colnames(exprs(rawData.summary)), "sv" = svobj.nonorm$sv)

head(sv_df.nonorm)
sv_df.classQN %>% 
ggplot() +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = er)) +
  # geom_point(mapping = aes(x = submission_date, y = sv, color = er)) +
  theme_light() 

  # labs(y = "Surrogate Variable Value", title = "Distribution of latent variable estimated by SVA for different grouping factors")
  

# ggsave("plots/exploration_plots/sva_grouping_classQN.png")
left_join(sv_df.nonorm, mdata_subset, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = pr, y = sv, fill = set)) +
  scale_fill_npg(name = "Sample Set") +
  theme_light() +
   labs(x = "Progesterone Receptor Status",
    y = "Surrogate Variable Value",
       title = str_wrap("Distribution of latent variable estimated by SVA for progesterone receptor status, coloured by sample set", 50))
 

ggsave("plots/exploration_plots/sva_grouping_pr_noQN.png")
Saving 5.03 x 3.11 in image

left_join(sv_df.nonorm, mdata_subset, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = er, y = sv, fill = set)) +
  scale_fill_npg(name = "Sample Set") +
  theme_light() +
   labs(x = "Estrogen Receptor Status",
    y = "Surrogate Variable Value",
       title = str_wrap("Distribution of latent variable estimated by SVA for estrogen receptor status, coloured by sample set", 50))
 

ggsave("plots/exploration_plots/sva_grouping_er_noQN.png")
Saving 5.03 x 3.11 in image

left_join(sv_df.nonorm, mdata_subset, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = her2, y = sv, fill = set)) +
  scale_fill_npg(name = "Sample Set") +
  theme_light() +
   labs(x = "HER2 Amplification Status",
    y = "Surrogate Variable Value",
       title = str_wrap("Distribution of latent variable estimated by SVA for HER2 amplification status, coloured by sample set", 50))
 

ggsave("plots/exploration_plots/sva_grouping_her2_noQN.png")
Saving 5.03 x 3.11 in image

left_join(sv_df.nonorm, mdata_subset, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  geom_point(mapping = aes(x = as.numeric(age_years), y = sv, color = set)) +
  scale_color_npg(name = "Sample set") +
  theme(axis.text.x = element_text(angle = 90, size = 7),
        title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) + 
      guides(color = guide_legend(override.aes = list(size = 1))) +
  labs(x = "Age in Years",
       y = "Surrogate Variable Value",
       title = str_wrap("Scatterplot of age latent variable estimated by SVA versus age", 50))
Warning: Removed 9 rows containing missing values (geom_point).
ggsave("plots/exploration_plots/sva_grouping_age_noQN.png")
Saving 5.03 x 3.11 in image
Warning: Removed 9 rows containing missing values (geom_point).

LS0tCnRpdGxlOiAiRXhwbG9yZSBHU0U3NjI3NSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgTG9hZGluZyBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KEdFT3F1ZXJ5KQpsaWJyYXJ5KG9saWdvKQpsaWJyYXJ5KHN2YSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShjYXJldCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShVcFNldFIpCmxpYnJhcnkoQ29tcGxleFVwc2V0KQpgYGAKCgojIEN1c3RvbSBmdW5jdGlvbnMKCmBgYHtyfQojIGdpdmVuIGEgbWF0cml4LCBwZXJmb3JtIG1pbi1tYXggc2NhbGluZyBvbiBpdHMgY29sdW1ucwptaW5fbWF4X21hdCA8LSBmdW5jdGlvbihtYXQpewogIG1hdF9yZXNjYWxlZCA8LSBhcHBseShtYXQsIDIsIGZ1bmN0aW9uKHYpewogICAgdl9yYW5nZSA8LSByYW5nZSh2KQogICAgbmFtZXModl9yYW5nZSkgPC0gYygibWluaW11bSIsICJtYXhpbXVtIikKICAgIHJhbmdlX2RpZmZlcmVuY2UgPC0gdl9yYW5nZVsibWF4aW11bSJdIC0gdl9yYW5nZVsibWluaW11bSJdCiAgICByZXNjYWxlZCA8LSAodiAtIHZfcmFuZ2VbIm1pbmltdW0iXSkvcmFuZ2VfZGlmZmVyZW5jZQogICAgcmV0dXJuKHJlc2NhbGVkKQogIH0pCiAgcmV0dXJuKG1hdF9yZXNjYWxlZCkKfQpgYGAKCiMgR2V0dGluZyBkYXRhIGZyb20gR0VPcXVlcnkKCmBgYHtyfQpnZW9kYXRhIDwtIEdFT3F1ZXJ5OjpnZXRHRU8oR0VPID0gIkdTRTc2Mjc1IiwgZGVzdGRpciA9ICIuL3RlbXBmaWxlcyIpCmBgYAoKCmBgYHtyfQptZGF0YSA8LSBnZW9kYXRhICU+JQogIHBsdWNrKDEpICU+JQogIHBoZW5vRGF0YSgpICU+JQogIHBEYXRhKCkgJT4lIGFzX3RpYmJsZSgpCmBgYAoKCmBgYHtyfQpmZWF0dXJlX2RhdGEgPC0gZ2VvZGF0YSAlPiUKICBwbHVjaygxKSAlPiUKICBmZWF0dXJlRGF0YSgpCiAgCmBgYAoKCmBgYHtyfQp3cml0ZV9jc3YobWRhdGEsICJyYXdfbWRhdGEuY3N2IikKYGBgCgoKIyBJbnNwZWN0aW5nIGFuZCBjbGVhbmluZyB0aGUgbWV0YWRhdGEKCmBgYHtyfQptZGF0YSAlPiUgCiAgZ2xpbXBzZSgpCmBgYAoKCmBgYHtyfQptZGF0YSA8LSBtZGF0YSAlPiUgCiAgc2VsZWN0KHRpdGxlLCBjb250YWlucygiZGF0ZSIpLCBnZW9fYWNjZXNzaW9uLCBjb250YWlucygiOmNoMSIpKQoKY29sbmFtZXMobWRhdGEpCiAgCmBgYAoKCmBgYHtyfQpjbmFtZXMgPC0gY29sbmFtZXMobWRhdGEpCmBgYAoKCmBgYHtyfQpjbmFtZXNfcHJvY2Vzc2VkIDwtIHN0cl9zcGxpdChjbmFtZXMsIHBhdHRlcm4gPSAiOiIpICU+JSAKICBtYXBfY2hyKH57LnhbWzFdXX0pICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIiAiLCAiXyIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIi0iLCAiXyIpICU+JSAKICBzdHJfcmVtb3ZlX2FsbCgiXFwofFxcKXwsIikKCmNuYW1lc19wcm9jZXNzZWQKYGBgCgoKYGBge3J9CmNvbG5hbWVzKG1kYXRhKSA8LSBjbmFtZXNfcHJvY2Vzc2VkCnJtKGNuYW1lcywgY25hbWVzX3Byb2Nlc3NlZCkKYGBgCgoKYGBge3J9CmdsaW1wc2UobWRhdGEpCmBgYAoKYGBge3J9Cm1kYXRhIDwtIG1kYXRhICU+JSAKICBtdXRhdGUoaGVyMiA9IGlmX2Vsc2UoIWlzLm5hKGhlcjIpLCBoZXIyLCAiTm90IEF2YWlsYWJsZSIpKSAlPiUgCiAgbXV0YXRlKGVyID0gZmFjdG9yKGVyLCBsZXZlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKSwgCiAgICAgICAgIHByID0gZmFjdG9yKHByLCBsZXZlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKSwgCiAgICAgICAgIGhlcjIgPSBmYWN0b3IoaGVyMiwgbGV2ZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiLCAiTm90IEF2YWlsYWJsZSIpKSkgJT4lIAogIHNlbGVjdChnZW9fYWNjZXNzaW9uLCBldmVyeXRoaW5nKCkpCmhlYWQobWRhdGEpIApgYGAKClJlb3JkZXJpbmcgdGhlIGNvbHVtbnMgaW4gdGhlIG1ldGFkYXRhLgoKYGBge3J9Cm1kYXRhIDwtIHNlbGVjdChtZGF0YSwgZ2VvX2FjY2Vzc2lvbiwgZXZlcnl0aGluZygpKQpoZWFkKG1kYXRhKQpgYGAKCiMgUmVhZGluZyBpbiByYXcgcHJvYmUgaW50ZW5zaXR5IGRhdGEKCkNlbGZpbGVzIGRvd25sb2FkZWQgZnJvbSBHRU8gYW5kIGtlcHQgdGhlIGZvbGRlciAiY2VsZmlsZXMvIi4KCmBgYHtyfQpjZWxGaWxlcyA8LSBsaXN0LmNlbGZpbGVzKCdjZWxmaWxlcy8nLCBmdWxsLm5hbWVzID0gVFJVRSwgbGlzdEd6aXBwZWQgPSBUUlVFKQpjZWxGaWxlcyAlPiUgaGVhZCgpCmBgYAoKCgpgYGB7cn0KbmFtZXMoY2VsRmlsZXMpIDwtIGNlbEZpbGVzICU+JSAKICBiYXNlbmFtZSgpICU+JSAKICBzdHJfc3BsaXQoIlxcLiIpICU+JSAKICBtYXBfY2hyKH57LnhbMV19KSAlPiUgCiAgc3RyX3NwbGl0KCJfIikgJT4lIAogIG1hcF9jaHIofnsueFsxXX0pIAoKaGVhZChjZWxGaWxlcykKYGBgCgpSZWFycmFuZ2luZyByb3dzIG9mIG1ldGFkYXRhIHRvIG1hdGNoIG9yZGVyIG9mIHNhbXBsZXMgaW4gYGNlbEZpbGVzYC4KCmBgYHtyfQptZGF0YSA8LSBtZGF0YVttYXRjaChtZGF0YSRnZW9fYWNjZXNzaW9uLCBuYW1lcyhjZWxGaWxlcykpLCBdCmBgYAoKCkdldHRpbmcgb25seSB0aGUgcmVsZXZhbnQgdmFyaWFibGVzIGZyb20gdGhlIG1ldGFkYXRhLgoKYGBge3J9Cm1kYXRhX3N1YnNldCA8LSBtZGF0YSAlPiUKICBzZWxlY3QoZ2VvX2FjY2Vzc2lvbiwgCiAgICAgICAgIHRpdGxlLCAKICAgICAgICAgdHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgCiAgICAgICAgIHRuYmNfc3VidHlwZSwKICAgICAgICAgc3VibWlzc2lvbl9kYXRlLAogICAgICAgICBlciwKICAgICAgICAgaGVyMiwKICAgICAgICAgcHIsCiAgICAgICAgIHJhY2UsCiAgICAgICAgIHNldCwKICAgICAgICAgZ2VuZGVyLCAKICAgICAgICAgYWdlX3llYXJzKSAlPiUgCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCAuZm5zID0gZmFjdG9yKSkgJT4lIAogIG11dGF0ZSh0bmJjX3N1YnR5cGUgPSBpZl9lbHNlKGlzLm5hKGFzLmNoYXJhY3Rlcih0bmJjX3N1YnR5cGUpKSwgIk5vdCBBcHBsaWNhYmxlIiwgYXMuY2hhcmFjdGVyKHRuYmNfc3VidHlwZSkpKSAlPiUgCiAgbXV0YXRlKHRuYmNfc3VidHlwZSA9IGZhY3Rvcih0bmJjX3N1YnR5cGUpKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgoKcm93bmFtZXMobWRhdGFfc3Vic2V0KSA8LSBhcy5jaGFyYWN0ZXIobWRhdGFfc3Vic2V0JGdlb19hY2Nlc3Npb24pCgpoZWFkKG1kYXRhX3N1YnNldCkKCmBgYAoKYGBge3J9CnJhd0RhdGEgPC0gcmVhZC5jZWxmaWxlcyhjZWxGaWxlcywgcGhlbm9EYXRhID0gQW5ub3RhdGVkRGF0YUZyYW1lKG1kYXRhX3N1YnNldCkpCmBgYAoKCkxvb2tpbmcgYXQgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIHJhdyBleHByZXNzaW9uIG1hdHJpeC4KCmBgYHtyfQpleHBycyhyYXdEYXRhKSAlPiUgZGltKCkKYGBgCgojIFBsb3R0aW5nIHNvbWUgbWV0YWRhdGEgYXR0cmlidXRlcwoKTG9va2luZyBhdCB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgZm9yIHRyaXBsZSBuZWdhdGl2ZSBzdGF0dXMgYW5kIGZvciBzZXQuCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBzZXQpCmBgYAoKCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSAlPiUgCiAgbXV0YXRlKHByb3BvcnRpb24gPSByb3VuZChuL3N1bShuKSwgMykpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh5ID0gImR1bW15X2dyb3VwIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBwcm9wb3J0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIGdlb21fdGV4dChhZXMoeSA9IDEsIAogICAgICAgICAgICAgICAgeCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgbGFiZWwgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGdyb3VwID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cyksIAogICAgICAgICAgICBzaXplID0gNSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgKwogIGxhYnMoeCA9ICJQcm9wb3J0aW9uIiwKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlByb3BvcnRpb25zIG9mIFROQkMgc3RhdHVzIHZhbHVlcyBmb3IgR1NFNzYyNzUiLCA2MCkpCgpnZ3NhdmUoZmlsZW5hbWUgPSAicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfdG5iY19wcm9wb3J0aW9uX2JhcnBsb3QucG5nIikKYGBgCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBzZXQpICU+JSAKICBtdXRhdGUocHJvcG9ydGlvbiA9IHJvdW5kKG4vc3VtKG4pLCAzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHByb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gc2V0KSkgKwogICAgZ2VvbV90ZXh0KGFlcyh4ID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgCiAgICAgICAgICAgICAgICB5ID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgZ3JvdXAgPSBzZXQpLCAKICAgICAgICAgICAgc2l6ZSA9IDMsIAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKwogICAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJTYW1wbGUgU2V0IikgKwogIGxhYnMoeCA9ICJUTkJDIFN0YXR1cyIsCiAgICAgICB5ID0gIlByb3BvcnRpb24iLAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiUHJvcG9ydGlvbnMgb2Ygc2FtcGxlIHNldCB2YWx1ZXMgZm9yIEdTRTc2Mjc1IiwgNjApKQoKZ2dzYXZlKGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3NldF9wcm9wb3J0aW9uX2JhcnBsb3QucG5nIikKYGBgCgoKCmBgYHtyfQptZGF0YV9zdWJzZXQgJT4lIAogIGZpbHRlcih0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJub3QgVE4iKSAlPiUgCiAgY291bnQocHIpICU+JSAKICBtdXRhdGUocHJvcG9ydGlvbiA9IHJvdW5kKG4vc3VtKG4pLCAzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHkgPSAiZHVtbXlfZ3JvdXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHByb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcHIgKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICBnZW9tX3RleHQoYWVzKHkgPSAxLCAKICAgICAgICAgICAgICAgIHggPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBncm91cCA9IHByICksIAogICAgICAgICAgICBzaXplID0gNSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJQcm9nZXN0ZXJvbmUgUmVjZXB0b3IgU3RhdHVzIikgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgKwogIGxhYnMoeCA9ICJQcm9wb3J0aW9uIiwKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlByb3BvcnRpb25zIG9mIHByb2dlc3Rlcm9uZSByZWNlcHRvciBzdGF0dXMgdmFsdWVzIGZvciBub24tVE5CQyBzYW1wbGVzIGluIEdTRTc2MjciLCA2MCkpCgoKZ2dzYXZlKGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3ByX3Byb3BvcnRpb25fYmFycGxvdC5wbmciKQpgYGAKCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBmaWx0ZXIodHJpcGxlX25lZ2F0aXZlX3N0YXR1cyA9PSAibm90IFROIikgJT4lIAogIGNvdW50KGVyKSAlPiUgIAogIG11dGF0ZShwcm9wb3J0aW9uID0gcm91bmQobi9zdW0obiksIDMpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeSA9ICJkdW1teV9ncm91cCIsCiAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcHJvcG9ydGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBlcikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gMSwgCiAgICAgICAgICAgICAgICB4ID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgZ3JvdXAgPSBlciksIAogICAgICAgICAgICBzaXplID0gNSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJFc3Ryb2dlbiBSZWNlcHRvciBTdGF0dXMiKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArCiAgbGFicyh4ID0gIlByb3BvcnRpb24iLAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiUHJvcG9ydGlvbnMgb2YgZXN0cm9nZW4gcmVjZXB0b3Igc3RhdHVzIHZhbHVlcyBmb3Igbm9uLVROQkMgc2FtcGxlcyBpbiBHU0U3NjI3IiwgNjApKQoKZ2dzYXZlKGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X2VyX3Byb3BvcnRpb25fYmFycGxvdC5wbmciKQpgYGAKCmBgYHtyfQptZGF0YV9zdWJzZXQgJT4lIAogIGZpbHRlcih0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJub3QgVE4iKSAlPiUgCiAgY291bnQoaGVyMikgJT4lICAKICBtdXRhdGUocHJvcG9ydGlvbiA9IHJvdW5kKG4vc3VtKG4pLCAzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHkgPSAiZHVtbXlfZ3JvdXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHByb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gaGVyMikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gMSwgCiAgICAgICAgICAgICAgICB4ID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgZ3JvdXAgPSBoZXIyKSwgCiAgICAgICAgICAgIHNpemUgPSA1LCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9maWxsX25wZyhuYW1lID0gIkhFUjIgQW1wbGlmaWNhdGlvbiBTdGF0dXMiKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArCiAgbGFicyh4ID0gIlByb3BvcnRpb24iLAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiUHJvcG9ydGlvbnMgb2YgSEVSMiBhbXBsaWZpY2F0aW9uIHN0YXR1cyB2YWx1ZXMgZm9yIG5vbi1UTkJDIHNhbXBsZXMgaW4gR1NFNzYyNyIsIDYwKSkKCmdnc2F2ZShmaWxlbmFtZSA9ICJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9HU0U3NjI3NV9oZXIyX3Byb3BvcnRpb25fYmFycGxvdC5wbmciKQpgYGAKCmBgYHtyfQptZGF0YV9zdWJzZXQgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QoYWVzKHggPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCB5ID0gYWdlX3llYXJzLCBmaWxsID0gc2V0KSkgKwogIHNjYWxlX2ZpbGxfbnBnKG5hbWUgPSAiU2FtcGxlIFNldCIpICsKICB0aGVtZV9saWdodCgpICsKICAgIGxhYnMoeCA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIiwKICAgICAgICAgeSA9ICJBZ2UgaW4gWWVhcnMiLAogICAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJBZ2UgZGlzdHJpYnV0aW9uIG9mIHNhbXBsZSBzZXRzIGZvciBHU0U3NjI3NSIsIDYwKSkKICAKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfYWdlX2JveHBsb3QucG5nIikKYGBgCgoKCmBgYHtyfQpsaXN0KCJFUiIgPSBtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvblttZGF0YV9zdWJzZXQkZXIgPT0gIlBvc2l0aXZlIl0sCiAgICAgIlBSIiA9IG1kYXRhX3N1YnNldCRnZW9fYWNjZXNzaW9uW21kYXRhX3N1YnNldCRwciA9PSAiUG9zaXRpdmUiXSwKICAgICAiSEVSMiIgPSBtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvblttZGF0YV9zdWJzZXQkaGVyMiA9PSAiUG9zaXRpdmUiXSkgJT4lCiAgZnJvbUxpc3QoLikgJT4lIAogIHVwc2V0KC4sIGMoIkVSIiwgIlBSIiwgIkhFUjIiKSwgd2lkdGhfcmF0aW8gPSAwLjIpICsKICBnZ3RpdGxlKHN0cl93cmFwKCJVcHNldCBwbG90IGZvciBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIEVSLCBQUiwgYW5kIEhFUjIgc3RhdHVzIGluIHRoZSBub24tVE5CQyBzYW1wbGVzIGluIEdTRTc2Mjc1IiwgNDApKSArCiAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfbm9uVE5CQ191cHNldC5wbmciKQpgYGAKCiMgUGVyZm9ybWluZyBSTUEKCiMjIFVzaW5nIHJlZ3VsYXIgUk1BIG9uIGRhdGEgKHdpdGhvdXQgc2VwYXJhdGluZyBieSBjbGFzcykKCmBgYHtyfQpyZXNfMSA8LSBybWEocmF3RGF0YSkKYGBgCgpMb29raW5nIGF0IHRoZSBkaW1lbnNpb25zIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeCBhZnRlciBSTUEuCgpgYGB7cn0KZXhwcnMocmVzXzEpICU+JSAKICBkaW0oKQpgYGAKCkxvb2tpbmcgYXQgdGhlIGZpcnN0IDUgcm93cyBhbmQgY29sdW1ucy4KCmBgYHtyfQpleHBycyhyZXNfMSlbMTo1LCAxOjVdCmBgYAoKCiMjIFBlcmZvcm1pbmcgY2xhc3Mtc3BlY2lmaWMgUk1BIGJ5IHJlYWRpbmcgaW4gdGhlIGV4cHJlc3Npb24gc2V0cyBzZXBhcmF0ZWx5CgoKR2V0dGluZyBsaXN0cyBvZiB0aGUgVE5CQyBzYW1wbGVzIGFuZCB0aGUgbm9uLVROQkMgc2FtcGxlcy4KCmBgYHtyfQp0bmJjX3NhbXBsZXMgPC0gbWRhdGFfc3Vic2V0ICU+JSAKICBmaWx0ZXIodHJpcGxlX25lZ2F0aXZlX3N0YXR1cyA9PSAiVE4iKSAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24pICU+JSAKICB1bmxpc3QodXNlLm5hbWVzID0gRikgJT4lIAogIGFzLmNoYXJhY3RlcigpCgpoZWFkKHRuYmNfc2FtcGxlcykKCm5vbnRuYmNfc2FtcGxlcyA8LSBtZGF0YV9zdWJzZXQgJT4lIAogIGZpbHRlcih0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJub3QgVE4iKSAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24pICU+JSAKICB1bmxpc3QodXNlLm5hbWVzID0gRikgJT4lIAogIGFzLmNoYXJhY3RlcigpCgpoZWFkKG5vbnRuYmNfc2FtcGxlcykKYGBgCgoKQ3JlYXRpbmcgZGlmZmVyZW50IG1ldGFkYXRhIHRhYmxlcyBmb3IgVE5CQyBhbmQgbm9uVE5CQy4KCmBgYHtyfQptZGF0YV9zdWJzZXRfdG5iYyA8LSBtZGF0YV9zdWJzZXRbdG5iY19zYW1wbGVzLCBdCmRpbShtZGF0YV9zdWJzZXRfdG5iYykKbWRhdGFfc3Vic2V0X25vbnRuYmMgPC0gbWRhdGFfc3Vic2V0W25vbnRuYmNfc2FtcGxlcywgXQpkaW0obWRhdGFfc3Vic2V0X25vbnRuYmMpCmBgYAoKClJlYWRpbmcgaW4gdGhlIFROQkMgZmlsZXMuCgpgYGB7cn0KcmF3RGF0YV90bmJjIDwtIHJlYWQuY2VsZmlsZXMoZmlsZW5hbWVzID0gY2VsRmlsZXNbdG5iY19zYW1wbGVzXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhID0gQW5ub3RhdGVkRGF0YUZyYW1lKG1kYXRhX3N1YnNldF90bmJjKSkKCnJhd0RhdGFfdG5iYwpgYGAKCgpSZWFkaW5nIGluIHRoZSBub25UTkJDIGZpbGVzLgoKYGBge3J9CnJhd0RhdGFfbm9udG5iYyA8LSByZWFkLmNlbGZpbGVzKGZpbGVuYW1lcyA9IGNlbEZpbGVzW25vbnRuYmNfc2FtcGxlc10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YSA9IEFubm90YXRlZERhdGFGcmFtZShtZGF0YV9zdWJzZXRfbm9udG5iYykpCgpyYXdEYXRhX25vbnRuYmMKYGBgCgoKCmBgYHtyfQpyYXdEYXRhX25vbnRuYmMKYGBgCgoKUGVyZm9ybWluZyBSTUEgb24gVE5CQyBkYXRhLgoKYGBge3J9CnJlc190bmJjIDwtIHJtYShyYXdEYXRhX3RuYmMpCmBgYAoKClBlcmZvcm1pbmcgUk1BIG9uIG5vblROQkMgZGF0YS4KCmBgYHtyfQpyZXNfbm9udG5iYyA8LSBybWEocmF3RGF0YV9ub250bmJjKQpgYGAKCgpDb21iaW5pbmcgdGhlIGV4cHJlc3Npb24gbWF0cmljZXMgb2YgVE5CQyBhbmQgbm9uVE5CQyBkYXRhIGFmdGVyIHNlcGFyYXRlIFJNQS4KCmBgYHtyfQpyZXNfam9pbnQgPC0gY2JpbmQoZXhwcnMocmVzX3RuYmMpLCBleHBycyhyZXNfbm9udG5iYykpCmBgYAoKTG9va2luZyBhdCB0aGUgZmlyc3QgNSByb3dzIGFuZCBjb2x1bW5zIG9mIHRoZSBjb21iaW5lZCBleHByZXNzaW9uIG1hdHJpY2VzLgoKYGBge3J9CnJlc19qb2ludFsxOjUsIDE6NV0KYGBgCgojIFNhdmluZyBjZXJ0YWluIENTViBmaWxlcyBmb3IgZXZlcnlvbmUgZWxzZSB0byByZWZlciB0bwoKU2F2aW5nIHRoZSBqb2ludCBleHByZXNzaW9uIG1hdHJpeCBmcm9tIGNsYXNzLXNwZWNpZmljIFFOLgoKYGBge3J9CnJlc19qb2ludCAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInByb2JlX2lkIikgJT4lIAogIHdyaXRlX2NzdigiZGF0YWZyYW1lX2ZpbGVzL3Bvc3RfY2xhc3NRTl9leHByZXNzaW9uLmNzdiIpCmBgYAoKU2F2aW5nIGEgc3Vic2V0IG9mIHRoZSBtZXRhZGF0YSB0aGF0IEkgdGhpbmsgaXMgcmVsZXZhbnQuCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICB3cml0ZV9jc3YoImRhdGFmcmFtZV9maWxlcy9tZXRhZGF0YV9zdWJzZXQuY3N2IikKYGBgCgoKU2F2aW5nIHRoZSBUTkJDIGFuZCBub25UTkJDIG1ldGFkYXRhIHNlcGFyYXRlbHksIGp1c3QgaW4gY2FzZS4KCmBgYHtyfQptZGF0YV9zdWJzZXRfdG5iYyAlPiUgCiAgd3JpdGVfY3N2KCJkYXRhZnJhbWVfZmlsZXMvbWV0YWRhdGFfc3Vic2V0X3RuYmMuY3N2IikKYGBgCgoKYGBge3J9Cm1kYXRhX3N1YnNldF9ub250bmJjICU+JSAKICB3cml0ZV9jc3YoImRhdGFmcmFtZV9maWxlcy9tZXRhZGF0YV9zdWJzZXRfbm9udG5iYy5jc3YiKQpgYGAKCgojIEdldHRpbmcgc2FtcGxlLXNwZWNpZmljIGJveHBsb3RzCgojIyBTYW1wbGUgc3BlY2lmaWMgYm94cGxvdHMgZm9yIHJlZ3VsYXIgUk1BIFFOCgoKYGBge3J9CnJlc18xX2RmX2xvbmcgPC0gcmVzXzEgJT4lCiAgZXhwcnMoKSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInByb2JlSUQiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbGxfb2YoYyh0bmJjX3NhbXBsZXMsIG5vbnRuYmNfc2FtcGxlcykpLCBuYW1lc190byA9ICJzYW1wbGVfaWQiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBsZWZ0X2pvaW4oLiwgbWRhdGFfc3Vic2V0LCBieSA9IGMoInNhbXBsZV9pZCIgPSAiZ2VvX2FjY2Vzc2lvbiIpKQpgYGAKCgoKYGBge3J9CnAyIDwtIHJlc18xX2RmX2xvbmcgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfaWQsIGFzLm51bWVyaWMoc2V0KSksIHkgPSBpbnRlbnNpdHksIGNvbG9yID0gc2V0KSwgb3V0bGllci5zaXplID0gMC4yKQpgYGAKCgpgYGB7cn0KcDIgPC0gcDIgKyBsYWJzKHggPSAic2FtcGxlcyIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiU2FtcGxlLXdpc2UgbG9nMiBpbnRlbnNpdHkgYm94cGxvdHMgZm9yIGdsb2JhbCBxdWFudGlsZSBub3JtYWxpemF0aW9uIGZvciBHU0U3NjI3NSIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpCnAyCiAgCmBgYAoKYGBge3J9Cmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfcG9zdF9yZWdRTl9ib3hwbG90cy5wbmciLCAKICAgICAgIHAyLCAKICAgICAgIHVuaXRzID0gImNtIiwgCiAgICAgICB3aWR0aCA9IDMwLCAKICAgICAgIGhlaWdodCA9IDEwKQpgYGAKCgpgYGB7cn0Kcm0ocmVzXzFfZGZfbG9uZykKYGBgCgoKIyMgU2FtcGxlIHNwZWNpZmljIGJveHBsb3RzIGZvciBjbGFzc1FOCgoKYGBge3J9CnJlc19qb2ludF9kZl9sb25nIDwtIHJlc19qb2ludCAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInByb2JlSUQiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbGxfb2YoYyh0bmJjX3NhbXBsZXMsIG5vbnRuYmNfc2FtcGxlcykpLCBuYW1lc190byA9ICJzYW1wbGVfaWQiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImludGVuc2l0eSIpCiAgCmBgYAoKCmBgYHtyfQpzYXZlUkRTKG9iamVjdCA9IHJlc19qb2ludF9kZl9sb25nLCAicmVzX2pvaW50X2RmX2xvbmcuUkRTIikKIyByZXNfam9pbnRfZGZfbG9uZyA8LSByZWFkUkRTKCJyZXNfam9pbnRfZGZfbG9uZy5SRFMiKQpgYGAKCgoKYGBge3J9CnAxIDwtIHJlc19qb2ludF9kZl9sb25nICU+JSAKICBsZWZ0X2pvaW4oLiwgbWRhdGFfc3Vic2V0LCBieSA9IGMoInNhbXBsZV9pZCIgPSAiZ2VvX2FjY2Vzc2lvbiIpKSAlPiUgCiAgbXV0YXRlKHNhbXBsZV9pZCA9IGZhY3RvcihzYW1wbGVfaWQpKSAlPiUgCiAgZ2dwbG90KCkgKwogICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfaWQsIGFzLm51bWVyaWMoc2V0KSksIHkgPSBpbnRlbnNpdHksIGNvbG9yID0gc2V0KSwgb3V0bGllci5zaXplID0gMC4yKQpgYGAKCgpgYGB7cn0KcDEgPC0gcDEgKyBsYWJzKHggPSAic2FtcGxlcyIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiU2FtcGxlLXdpc2UgbG9nMiBpbnRlbnNpdHkgYm94cGxvdHMgZm9yIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24gZm9yIEdTRTc2Mjc1IiwgNjApKSArCnNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpCgpwMSAKYGBgCgoKCmBgYHtyfQpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3Bvc3RfY2xhc3NRTl9ib3hwbG90cy5wbmciLCAKICAgICAgIHAxLCAKICAgICAgIHVuaXRzID0gImNtIiwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMTApCmBgYAoKCgojIFBlcmZvcm1pbmcgUENBCgojIyBDdXN0b20gZnVuY3Rpb25zCgpGdW5jdGlvbiB0byBjcmVhdGUgYW4gYW5ub3RhdGVkIGRhdGEgZnJhbWUgYnkgY29tYmluaW5nIFBDIHNjb3JlcyBhcyB3ZWxsIGFzIG1ldGFkYXRhOiB1c2VmdWwgZm9yIGdncGxvdCB2aXN1YWxpemF0aW9uLgoKYGBge3J9CmdldF9wY2FfYW5ub3RfZGYgPC0gZnVuY3Rpb24ocGNhLm9iaiwgc2FtcGxlX2lkX2NvbCwgbWRhdGFfZGYpewogIGluZF9zY29yZXMgPC0gcGNhLm9iaiR4CiAgaW5kX3Njb3Jlc19yZW9yZGVyZWQgPC0gaW5kX3Njb3Jlc1ttYXRjaChyb3duYW1lcyhpbmRfc2NvcmVzKSwgbWRhdGFfZGZbW3NhbXBsZV9pZF9jb2xdXSksIF0gJT4lIAogICAgYXNfdGliYmxlKHJvd25hbWVzID0gc2FtcGxlX2lkX2NvbCkgJT4lIAogICAgbXV0YXRlKGZpbGVuYW1lID0gZmFjdG9yKCEhc3ltKHNhbXBsZV9pZF9jb2wpKSkKICBpbmRfc2NvcmVzX2Fubm90IDwtIGxlZnRfam9pbihpbmRfc2NvcmVzX3Jlb3JkZXJlZCwgeSA9IG1kYXRhX2RmLCBieSA9IHNhbXBsZV9pZF9jb2wpICU+JSAKICBzZWxlY3QoYWxsX29mKGNvbG5hbWVzKG1kYXRhX3N1YnNldCkpLCBjb250YWlucygiUEMiKSkKICByZXR1cm4oaW5kX3Njb3Jlc19hbm5vdCkKfQpgYGAKCgojIyBQZXJmb3JtaW5nIFBDQSBvbiByZWd1bGFyIFJNQSBkYXRhCgpgYGB7cn0KcGNhLnJlc18xIDwtIHJlc18xICU+JQogIGV4cHJzKCkgJT4lCiAgdCgpICU+JQogIHByY29tcChjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCmBgYAoKCkdldHRpbmcgdGhlIGFubm90YXRlZCBkYXRhIGZyYW1lIGZvciB0aGUgUENBLgoKYGBge3J9CnBjYS5yZXNfMS5hbm5vdF9kZiA8LSBnZXRfcGNhX2Fubm90X2RmKHBjYS5vYmogPSBwY2EucmVzXzEsIHNhbXBsZV9pZF9jb2wgPSAiZ2VvX2FjY2Vzc2lvbiIsIG1kYXRhX2RmPSBtZGF0YV9zdWJzZXQpCmhlYWQocGNhLnJlc18xLmFubm90X2RmKQpgYGAKCiMjIyBWaXN1YWxpemluZyBQQ0EgcmVzdWx0cwoKTG9va2luZyBhdCB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCAxMCBQQ3MuCgpgYGB7cn0KZnZpel9laWcocGNhLnJlc18xKSArCiAgbGFicyh4ID0gIlByaW5jaXBhbCBDb21wb25lbnQiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNjcmVlIHBsb3QgZm9yIHRoZSBmaXJzdCAxMCBwcmluY2lwYWwgY29tcG9uZW50cyBmb3IgZ2xvYmFsIFJNQS1ub3JtYWxpemVkIGRhdGEgZm9yIEdTRTc2Mjc1IiwgNjApKSArCiAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV93aG9sZVFOX3NjcmVlLnBuZyIsIGJnID0gIndoaXRlIikKYGBgCgpTdXBlcmltcG9zaW5nIHZhcmlhYmxlcyBpbiBkYXRhIHVwb24gc2FtcGxlIFBDQSBzY29yZXMuCgpUaGUgUENBIGRvZXMgbm90IHNlZW0gdG8gc2VwYXJhdGUgdGhlIFROQkMgYW5kIG5vblROQkMgc2FtcGxlcyB0aGF0IHdlbGwgd2hlbiByZWd1bGFyIFJNQSBpcyBwZXJmb3JtZWQuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfMS5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykpICsKICAgIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgdHJpcGxlIG5lZ2F0aXZlIHN0YXR1cyBmb3IgR1NFNzYyNzUgYWZ0ZXIgZ2xvYmFsIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gKQoKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX3dob2xlUU5fVE5CQ19zdGF0dXMucG5nIiwgYmcgPSAid2hpdGUiKQogIApgYGAKCgoKVGhlIHNhbXBsZXMgZG8gbm90IHNlZW0gdG8gc2VwYXJhdGUgd2VsbCBieSBzZXQgZWl0aGVyLgoKYGBge3J9CmdncGxvdChwY2EucmVzXzEuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHNldCkpICsKICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IHNldCAoZGlzY292ZXJ5IG9yIHZhbGlkYXRpb24pIGZvciBHU0U3NjI3NSBhZnRlciBnbG9iYWwgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX2FhYXMobmFtZSA9ICJTYW1wbGUgU2V0IikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0Ffd2hvbGVRTl9zZXQucG5nIiwgYmcgPSAid2hpdGUiKQpgYGAKCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfMS5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gdG5iY19zdWJ0eXBlKSkgKwogICAgZ2d0aXRsZSgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBcbmNvbG91cmVkIGJ5IHRuYmNfc3VidHlwZSBmb3Igd2hvbGUgUU4iKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCh4ID0gMC41LCB1bml0cyA9ICJjbSIpKSAKYGBgCgojIyBQZXJmb3JtaW5nIFBDQSBvbiBzZXBhcmF0ZWx5LXBlcmZvcm1lZCBSTUEgZGF0YQoKYGBge3J9CnBjYS5yZXNfam9pbnQgPC0gcmVzX2pvaW50ICU+JSAKICB0KCkgJT4lIAogIHByY29tcChjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMocGNhLnJlc19qb2ludCwgInBjYV9yZXNfam9pbnQuUkRTIikKcGNhLnJlc19qb2ludCA8LSByZWFkUkRTKCJwY2FfcmVzX2pvaW50LlJEUyIpCmBgYAoKCkdldHRpbmcgdGhlIGFubm90YXRlZCBkYXRhIGZyYW1lIGZvciB0aGUgUENBLgoKYGBge3J9CnBjYS5yZXNfam9pbnQuYW5ub3RfZGYgPC0gZ2V0X3BjYV9hbm5vdF9kZihwY2Eub2JqID0gcGNhLnJlc19qb2ludCwgc2FtcGxlX2lkX2NvbCA9ICJnZW9fYWNjZXNzaW9uIiwgbWRhdGFfZGY9IG1kYXRhX3N1YnNldCkKaGVhZChwY2EucmVzX2pvaW50LmFubm90X2RmKQpgYGAKCiMjIyBWaXN1YWxpemluZyBQQ0EgcmVzdWx0cwoKTG9va2luZyBhdCB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCAxMCBQQ3MuCgpgYGB7cn0KZnZpel9laWcocGNhLnJlc19qb2ludCkgKwogIGxhYnMoeCA9ICJQcmluY2lwYWwgQ29tcG9uZW50IiwgCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJTY3JlZSBwbG90IGZvciB0aGUgZmlyc3QgMTAgcHJpbmNpcGFsIGNvbXBvbmVudHMgZm9yIGNsYXNzLXNwZWNpZmljIFJNQS1ub3JtYWxpemVkIGRhdGEgZm9yIEdTRTc2Mjc1IiwgNjApKSArCiAgICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX3NjcmVlLnBuZyIpCmBgYAoKU3VwZXJpbXBvc2luZyB2YXJpYWJsZXMgaW4gZGF0YSB1cG9uIHNhbXBsZSBQQ0Egc2NvcmVzLgpUaGUgUENBICoqZG9lcyoqIHNlcGFyYXRlIHRoZSBUTkJDIGFuZCBub25UTkJDIHNhbXBsZXMgd2VsbCB3aGVuIGNsYXNzLXNwZWNpZmljIFJNQSBpcyBwZXJmb3JtZWQuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IHRyaXBsZSBuZWdhdGl2ZSBzdGF0dXMgZm9yIEdTRTc2Mjc1IGFmdGVyIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9UTkJDX3N0YXR1cy5wbmciKQpgYGAKVGhlIHZhbGlkYXRpb24gbm9uVE5CQyBzYW1wbGVzIGFyZSBzZXBhcmF0ZWQgZnJvbSB0aGUgZGlzY292ZXJ5IFROQkMgYW5kIHZhbGlkYXRpb24gVE5CQyBzYW1wbGVzLgoKYGBge3J9CmdncGxvdChwY2EucmVzX2pvaW50LmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSBzZXQpKSArCiAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBzZXQgKGRpc2NvdmVyeSBvciB2YWxpZGF0aW9uKSBmb3IgR1NFNzYyNzUgYWZ0ZXIgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX2FhYXMobmFtZSA9ICJTYW1wbGUgU2V0IikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX1ROQkNfc2V0LnBuZyIpCmBgYAoKU3VibWlzc2lvbiBkYXRlIGlzIHBlcmZlY3RseSBjb25mb3VuZGVkIHdpdGggVE5CQyBzdGF0dXMuIE1heSBvciBtYXkgbm90IGJlIGJhdGNoIGVmZmVjdHMuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHN1Ym1pc3Npb25fZGF0ZSkpICsKICAgIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgc3VibWlzc2lvbiBkYXRlIGZvciBHU0U3NjI3NSBhZnRlciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiU3VibWlzc2lvbiBEYXRlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9UTkJDX2RhdGUucG5nIikKYGBgCgoKCiMgUGVyZm9ybWluZyBoaWVyYXJjaGljYWwgY2x1c3RlcmluZwoKIyMgR2V0dGluZyBkaXN0YW5jZXMKCmBgYHtyfQpwZXJmb3JtX21pbl9tYXggPC0gZnVuY3Rpb24oeCl7CiAgbW1fdHJhbnNmb3JtYXRpb24gPC0gcHJlUHJvY2Vzcyh4LCBtZXRob2QgPSAicmFuZ2UiKQogIHJlc2NhbGVkIDwtIHByZWRpY3QobW1fdHJhbnNmb3JtYXRpb24sIHgpCiAgcmV0dXJuKHJlc2NhbGVkKQp9CmBgYAoKCkdldHRpbmcgZGlzdGFuY2VzIGFmdGVyIHBlcmZvcm1pbmcgbWluIG1heCBub3JtYWxpemF0aW9uLgoKCmBgYHtyfQpyZXNfam9pbnRfZGlzdHMgPC0gcmVzX2pvaW50ICU+JQogICAgdCgpICU+JQogIHBlcmZvcm1fbWluX21heCgpICU+JQogIGRpc3QobWV0aG9kID0gImV1Y2xpZGVhbiIpCiAgCmBgYAoKIyMgVXNpbmcgaGVhdG1hcHMKCkZ1bmN0aW9uIHRvIHByb2Nlc3MgZGlzdGFuY2Ugb2JqZWN0IGludG8gYSBkaXN0YW5jZSBtYXRyaXggZm9yIGhlYXRtYXAgdmlzdWFsaXphdGlvbi4KCmBgYHtyfQpnZXRfZGlzdG1hdCA8LSBmdW5jdGlvbih4KXsKICBkaXN0bWF0IDwtIGFzLm1hdHJpeCh4KQogIGNvbG5hbWVzKGRpc3RtYXQpIDwtIE5VTEwKICBkaWFnKGRpc3RtYXQpIDwtIE5BCiAgcmV0dXJuKGRpc3RtYXQpCn0KYGBgCgoKCmBgYHtyfQpyb3dfYW5ub3QgPC0gbWRhdGFfc3Vic2V0ICU+JSAKICBtdXRhdGUoZXIgPSBmYWN0b3IoZXIpLCBwciA9IGZhY3RvcihwciksIGhlcjIgPSBmYWN0b3IoaGVyMikpICU+JSAKICBzZWxlY3Qoc2V0LCBzdWJtaXNzaW9uX2RhdGUsIHByLCBlciwgaGVyMikgJT4lIAogICAgcmVuYW1lKGBTYW1wbGUgc2V0YCA9IHNldCwgCiAgICAgICAgICAgYFN1Ym1pc3Npb24gZGF0ZWAgPSBzdWJtaXNzaW9uX2RhdGUsCiAgICAgICAgICAgYEhFUjIgc3RhdHVzYCA9IGhlcjIsCiAgICAgICAgICAgYEVSIHN0YXR1c2AgPSBlciwKICAgICAgICAgICBgUFIgc3RhdHVzYCA9IHByKQoKaGVhZChyb3dfYW5ub3QpCmBgYAoKCmBgYHtyfQpzZXQuc2VlZCg1KQpyb3dfY29sb3VycyA8LSBsaXN0KCAiU2FtcGxlIHNldCIgPSBjKCJkYXJrZ29sZGVucm9kNCIsICJkYXJrbWFnZW50YSIsICJzYWxtb24iKSwgCiAgICAgICAgICAgICAgICAgICAgICJTdWJtaXNzaW9uIGRhdGUiID0gcGFsX25lam0oKSgyKSwKICAgICAgICAgICAgICAgICAgICAgIlBSIHN0YXR1cyIgPSBwYWxfbGFuY2V0KCkoOSlbMToyXSwgCiAgICAgICAgICAgICAgICAgICAgICJFUiBzdGF0dXMiID0gcGFsX2xhbmNldCgpKDkpWzg6OV0sCiAgICAgICAgICAgICAgICAgICAgICJIRVIyIHN0YXR1cyIgPSBwYWxfbnBnKCkoOSlbYygzLCAyLCA4KV0pCgpuYW1lcyhyb3dfY29sb3Vyc1tbIlNhbXBsZSBzZXQiXV0pIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUocm93X2Fubm90W1siU2FtcGxlIHNldCJdXSkpCm5hbWVzKHJvd19jb2xvdXJzW1siUFIgc3RhdHVzIl1dKSA8LSB1bmlxdWUocm93X2Fubm90W1siUFIgc3RhdHVzIl1dKQpuYW1lcyhyb3dfY29sb3Vyc1tbIkVSIHN0YXR1cyJdXSkgPC0gdW5pcXVlKHJvd19hbm5vdFtbIkVSIHN0YXR1cyJdXSkKbmFtZXMocm93X2NvbG91cnNbWyJIRVIyIHN0YXR1cyJdXSkgPC0gdW5pcXVlKHJvd19hbm5vdFtbIkhFUjIgc3RhdHVzIl1dKQpuYW1lcyhyb3dfY29sb3Vyc1tbIlN1Ym1pc3Npb24gZGF0ZSJdXSkgPC0gYXMuY2hhcmFjdGVyKHVuaXF1ZShyb3dfYW5ub3RbWyJTdWJtaXNzaW9uIGRhdGUiXV0pKQpzdHIocm93X2NvbG91cnMpCmBgYApgYGB7cn0KbXlfY29sb3VycyA8LSAgdmlyaWRpcygyNjVeMiwgYmVnaW4gPSAxLCBlbmQgPSAwKQpgYGAKCmBgYHtyfQpyZXNfam9pbnRfZGlzdHMgJT4lIAogIGdldF9kaXN0bWF0KCkgJT4lIApwaGVhdG1hcCguLAogICAgICAgICBjb2xvciA9IG15X2NvbG91cnMsCiAgICAgICAgIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90LAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IHJvd19jb2xvdXJzLAogICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMiwKICAgICAgICAgY3V0cmVlX2NvbHMgPSAyLAogICAgICAgICBtYWluID0gc3RyX3dyYXAoIkhlYXRtYXAgb2Ygc2FtcGxlIGRpc3RhbmNlcyBmb3IgY2xhc3Mtc3BlY2lmaWMgUU4gZXhwcmVzc2lvbiBtYXRyaXggZm9yIEdTRTc2Mjc1IiwgNjApLAogICAgICAgICBsZWdlbmRfbGFiZWxzID0gYygic21hbGwgZGlzdGFuY2UiLCAibGFyZ2UgZGlzdGFuY2UiKSwKICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IGMobWluKC4sIG5hLnJtID0gVFJVRSksIAogICAgICAgICAgICAgICAgICAgICAgICAgbWF4KC4sIG5hLnJtID0gVFJVRSkpLCAKICAgICAgICAgIGZpbGVuYW1lID0gInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL2NsYXNzUU5fY2x1c3RlcmluZ19oZWF0bWFwX0dTRTc2Mjc1LnBuZyIpCmBgYAoKCiMgUGVyZm9ybWluZyBTVkEKCgpJbiB0aGlzIGF0dGVtcHQsIEkgcGVyZm9ybSBubyBxdWFudGlsZSBub3JtYWxpemF0aW9uIHdoaWxlIHBlcmZvcm1pbmcgUk1BLiBJZiBRTiBoYXMgbm90IGJlZW4gcGVyZm9ybWVkIGFuZCBhIHN1cnJvZ2F0ZSB2YXJpYWJsZSBzaG93cyB1cCB0aGF0IGNvcnJlc3BvbmRzIHRvIGJhdGNoLCBiYXRjaCBlZmZlY3RzIGFyZSBwcm9iYWJseSBwcmVzZW50LgoKYGBge3J9CnJhd0RhdGEuc3VtbWFyeSA8LSBybWEocmF3RGF0YSwgYmFja2dyb3VuZCA9IFRSVUUsIG5vcm1hbGl6ZSA9IEZBTFNFKQpgYGAKCgoKYGBge3J9CnJhd0RhdGEuc3VtbWFyeV9kZl9sb25nIDwtIHJhd0RhdGEuc3VtbWFyeSAlPiUgCiAgZXhwcnMoKSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInByb2JlSUQiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbGxfb2YoYyh0bmJjX3NhbXBsZXMsIG5vbnRuYmNfc2FtcGxlcykpLCBuYW1lc190byA9ICJzYW1wbGVfaWQiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBsZWZ0X2pvaW4oLiwgbWRhdGFfc3Vic2V0LCBieSA9IGMoInNhbXBsZV9pZCIgPSAiZ2VvX2FjY2Vzc2lvbiIpKQpgYGAKCgpgYGB7cn0KcDMgPC0gcmF3RGF0YS5zdW1tYXJ5X2RmX2xvbmcgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfaWQsIGFzLm51bWVyaWMoc2V0KSksIHkgPSBpbnRlbnNpdHksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gc2V0KSkKYGBgCgoKYGBge3J9CnAzIDwtIHAzICsgbGFicyh4ID0gInNhbXBsZXMiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNhbXBsZS13aXNlIGxvZzIgaW50ZW5zaXR5IGJveHBsb3RzIGZvciBub24tIHF1YW50aWxlIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIEdTRTc2Mjc1IiwgNjApKSArCiAgc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikKCnAzCmBgYAoKCmBgYHtyfQpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X25vUU5fYm94cGxvdHMucG5nIiwgCiAgICAgICBwMywgCiAgICAgICB1bml0cyA9ICJjbSIsIHdpZHRoID0gMzAsIGhlaWdodCA9IDEwKQpgYGAKCgpDcmVhdGUgZnVsbCBtb2RlbCBtYXRyaXguCgpgYGB7cn0KZnVsbF9tb2QgPC0gbWRhdGFfc3Vic2V0ICU+JSAKICBzZWxlY3QoZ2VvX2FjY2Vzc2lvbiwgdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykgJT4lIAogIGFycmFuZ2UodHJpcGxlX25lZ2F0aXZlX3N0YXR1cykgJT4lIAogIG1vZGVsLm1hdHJpeCh+dHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgZGF0YSA9IC4pCgpoZWFkKGZ1bGxfbW9kKQpgYGAKCkNyZWF0ZSByZWR1Y2VkIG1vZGVsIG1hdHJpeC4KCmBgYHtyfQpyZWRfbW9kIDwtIG1vZGVsLm1hdHJpeCh+MSwgZGF0YSA9IG1kYXRhX3N1YnNldCkKCmhlYWQocmVkX21vZCkKYGBgCgpHZXR0aW5nIHRoZSBudW1iZXIgb2Ygc3Vycm9nYXRlIHZhcmlhYmxlcyBpbiB0aGUgYWJzZW5jZSBvZiBxdWFudGlsZSBub3JtYWxpemF0aW9uLgoKYGBge3J9Cm4uc3Yubm9ub3JtIDwtIG51bS5zdihleHBycyhyYXdEYXRhLnN1bW1hcnkpLCBmdWxsX21vZCwgbWV0aG9kPSJsZWVrIikKYGBgCgpUaGVyZSBpcyBvbmUgc3Vycm9nYXRlIHZhcmlhYmxlIHByZXNlbnQgaW4gdGhlIGFic2VuY2Ugb2YgUU4uCgpgYGB7cn0Kbi5zdi5ub25vcm0KYGBgCgoKCmBgYHtyfQpzdm9iai5ub25vcm0gPC0gc3ZhKGV4cHJzKHJhd0RhdGEuc3VtbWFyeSksIG1vZCA9IGZ1bGxfbW9kLCBtb2QwID0gcmVkX21vZCwgbi5zdiA9IG4uc3Yubm9ub3JtKQpgYGAKCgpgYGB7cn0Kc3ZfZGYubm9ub3JtIDwtIHRpYmJsZSgiZ2VvX2FjY2Vzc2lvbiIgPSBjb2xuYW1lcyhleHBycyhyYXdEYXRhLnN1bW1hcnkpKSwgInN2IiA9IHN2b2JqLm5vbm9ybSRzdikKCmhlYWQoc3ZfZGYubm9ub3JtKQpgYGAKCgpgYGB7cn0KbGVmdF9qb2luKHN2X2RmLm5vbm9ybSwgbWRhdGFfc3Vic2V0LCBieSA9ICJnZW9fYWNjZXNzaW9uIikgJT4lIAogIG11dGF0ZShpbmRleCA9IDUpICU+JSAKICBnZ3Bsb3QoKSArCiAgIyBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHkgPSBmY3RfcmVvcmRlcihnZW9fYWNjZXNzaW9uLCBzdiwgLmZ1biA9IGZ1bmN0aW9uKHgpe3h9KSwgeCA9IHN2LCBmaWxsID0gc2V0KSkgKwogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBzdWJtaXNzaW9uX2RhdGUsIHkgPSBzdiwgZmlsbCA9IHNldCkpICsKICBzY2FsZV9maWxsX25wZyhuYW1lID0gIlNhbXBsZSBTZXQiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgbGFicyh4ID0gIlN1Ym1pc3Npb24gRGF0ZSIsIAogICAgICAgICB5ID0gIlN1cnJvZ2F0ZSBWYXJpYWJsZSBWYWx1ZSIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiRGlzdHJpYnV0aW9uIG9mIGxhdGVudCB2YXJpYWJsZSBlc3RpbWF0ZWQgYnkgU1ZBIGZvciBzdWJtaXNzaW9uIGRhdGVzLCBjb2xvdXJlZCBieSBzYW1wbGUgc2V0IiwgNTApKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9zdmFfZ3JvdXBpbmdfZGF0ZV9ub1FOLnBuZyIpCmBgYAoKCmBgYHtyfQpsZWZ0X2pvaW4oc3ZfZGYubm9ub3JtLCBtZGF0YV9zdWJzZXQsIGJ5ID0gImdlb19hY2Nlc3Npb24iKSAlPiUgCiAgbXV0YXRlKGluZGV4ID0gNSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcHIsIHkgPSBzdiwgZmlsbCA9IHNldCkpICsKICBzY2FsZV9maWxsX25wZyhuYW1lID0gIlNhbXBsZSBTZXQiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgIGxhYnMoeCA9ICJQcm9nZXN0ZXJvbmUgUmVjZXB0b3IgU3RhdHVzIiwKICAgIHkgPSAiU3Vycm9nYXRlIFZhcmlhYmxlIFZhbHVlIiwKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIkRpc3RyaWJ1dGlvbiBvZiBsYXRlbnQgdmFyaWFibGUgZXN0aW1hdGVkIGJ5IFNWQSBmb3IgcHJvZ2VzdGVyb25lIHJlY2VwdG9yIHN0YXR1cywgY29sb3VyZWQgYnkgc2FtcGxlIHNldCIsIDUwKSkKIAoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9zdmFfZ3JvdXBpbmdfcHJfbm9RTi5wbmciKQpgYGAKCgpgYGB7cn0KbGVmdF9qb2luKHN2X2RmLm5vbm9ybSwgbWRhdGFfc3Vic2V0LCBieSA9ICJnZW9fYWNjZXNzaW9uIikgJT4lIAogIG11dGF0ZShpbmRleCA9IDUpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGVyLCB5ID0gc3YsIGZpbGwgPSBzZXQpKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJTYW1wbGUgU2V0IikgKwogIHRoZW1lX2xpZ2h0KCkgKwogICBsYWJzKHggPSAiRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIiwKICAgIHkgPSAiU3Vycm9nYXRlIFZhcmlhYmxlIFZhbHVlIiwKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIkRpc3RyaWJ1dGlvbiBvZiBsYXRlbnQgdmFyaWFibGUgZXN0aW1hdGVkIGJ5IFNWQSBmb3IgZXN0cm9nZW4gcmVjZXB0b3Igc3RhdHVzLCBjb2xvdXJlZCBieSBzYW1wbGUgc2V0IiwgNTApKQogCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL3N2YV9ncm91cGluZ19lcl9ub1FOLnBuZyIpCmBgYAoKYGBge3J9CmxlZnRfam9pbihzdl9kZi5ub25vcm0sIG1kYXRhX3N1YnNldCwgYnkgPSAiZ2VvX2FjY2Vzc2lvbiIpICU+JSAKICBtdXRhdGUoaW5kZXggPSA1KSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBoZXIyLCB5ID0gc3YsIGZpbGwgPSBzZXQpKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJTYW1wbGUgU2V0IikgKwogIHRoZW1lX2xpZ2h0KCkgKwogICBsYWJzKHggPSAiSEVSMiBBbXBsaWZpY2F0aW9uIFN0YXR1cyIsCiAgICB5ID0gIlN1cnJvZ2F0ZSBWYXJpYWJsZSBWYWx1ZSIsCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJEaXN0cmlidXRpb24gb2YgbGF0ZW50IHZhcmlhYmxlIGVzdGltYXRlZCBieSBTVkEgZm9yIEhFUjIgYW1wbGlmaWNhdGlvbiBzdGF0dXMsIGNvbG91cmVkIGJ5IHNhbXBsZSBzZXQiLCA1MCkpCiAKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvc3ZhX2dyb3VwaW5nX2hlcjJfbm9RTi5wbmciKQpgYGAKCgoKYGBge3J9CmxlZnRfam9pbihzdl9kZi5ub25vcm0sIG1kYXRhX3N1YnNldCwgYnkgPSAiZ2VvX2FjY2Vzc2lvbiIpICU+JSAKICBtdXRhdGUoaW5kZXggPSA1KSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gYXMubnVtZXJpYyhhZ2VfeWVhcnMpLCB5ID0gc3YsIGNvbG9yID0gc2V0KSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlNhbXBsZSBzZXQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUpLCAKICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCh4ID0gMC4zLCB1bml0cyA9ICJjbSIpKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMSkpKSArCiAgbGFicyh4ID0gIkFnZSBpbiBZZWFycyIsCiAgICAgICB5ID0gIlN1cnJvZ2F0ZSBWYXJpYWJsZSBWYWx1ZSIsCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJTY2F0dGVycGxvdCBvZiBhZ2UgbGF0ZW50IHZhcmlhYmxlIGVzdGltYXRlZCBieSBTVkEgdmVyc3VzIGFnZSIsIDUwKSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvc3ZhX2dyb3VwaW5nX2FnZV9ub1FOLnBuZyIpCmBgYAoK